Knuckle Cracker

Creeper World 3 => The Coder's Corner => Topic started by: eduran on January 31, 2014, 09:26:39 AM

Title: Flyer Movement Framework
Post by: eduran on January 31, 2014, 09:26:39 AM
I am starting this thread to continue the discussion about flying units started here (http://knucklecracker.com/forums/index.php?topic=15363.0).

Quote from: eduran on January 30, 2014, 04:18:05 PM
Quote from: virgilw on January 30, 2014, 02:47:41 PM
Things like air bases
Totally off-topic, but I am currently revisiting my River Crossing map, with the intention of replacing the swimming Leeches with airborne ones. Does an airport with a landing strip sound like a good idea? Sure, that can't be too hard to do. Except... getting the Leeches to actually point in the right direction when reaching a waypoint (the start of the landing strip) is pretty complicated.
The screenshot shows my best attempt so far: The Leech is supposed to face to the right when it reaches the 'approach point' (the cell at the foot of the arrow). To do that it checks the length of four possible paths and chooses the shortest of the lot (in this case, the yellow one). Working on that really makes me appreciate the effort that must have gone into allowing Bombers/Strafers to follow a player-drawn line.

I've made a lot of progress. The flyers are now able to follow arbitrary lines and they even do so by following the shortest possible path (I think). But instead of using it in a map, I am currently trying to create a reusable framework for flyer movement. The idea is to end up with a script that is attached to each flyer and can be used in the same way as the built-in movement commands.
I've attached the current version of that script (FlyerMovement.crpl) and an example script of how it is used, as well as a map to see everything in action.

Here is a basic example:
# move to random cell, pick new target on arrival
if(@GetQueuedMoveCount eq0)
RandCoords ->y ->x
@QueueMove(<-x <-y -1 <-speed <-accel -1)
endif

:QueueMove
# arguments: X and Y (cell coordinates), direction on arrival, speed (pixel per tick), acceleration (change in speed per tick), turn radius (pixels)
# direction has to be a positive angle (0 to 2*PI), set to -1 to allow any direction
# speed, acceleration and turn radius can be set to -1 to keep them unchanged
# x1 y1 f1 f2 f3 f4 -


As you can see, the custom QueueMove function allows for some extra arguments. The most important one is turn radius. While the build-in function moves towards the target in a straight line, the custom function will turn towards it first, following the specified turn radius.
If 'direction on arrival' is set, the flyer will approach the target cell in such a way that it ends up facing in the specified direction once it reaches the target.

ExampleFlyer.crpl contains two other, more elaborate examples. One draws a line between two randomly chosen endpoints and flies along this line. The other has the flyer land and roll around a makeshift airport, before it takes off again. FlyerDemo.cw3 has everything set up and ready to go (it defaults to the third example, this can be changed by setting 'example' to 0,1 or 2)

Why am I posting this? Mostly because I'd like to know if pursuing this further is worthwhile. Say I manage to make it almost as easy to access as the built-in commands, would anyone use it?



Edit: Updated the attached files and added Flyer.crpl. It is meant as a starting point for anyone who wants to use the scripts and contains a short explanation of how to set things up properly.
Title: Re: Flyer Movement Framework
Post by: knucracker on January 31, 2014, 09:46:14 AM
Awesome.... super awesome.  This could be used to make many very interesting things.
- Player owned air bases with one flyer each that when powered will automatically take off and make AC bombing runs...
- Player owned aircraft that intercept and take out spores (they swing around being them and fly up and shoot them down)
- Automated resource delivery drones that pick up AC on one island and deliver it to another (like AC guppies, but they actually carry AC not packets).
- A roving carrier that circles islands in a fixed path that launches aircraft to take out coastal structures.  Then the aircraft return to land.
Title: Re: Flyer Movement Framework
Post by: Helper on January 31, 2014, 11:00:47 AM
"Mostly because I'd like to know if pursuing this further is worthwhile"

Only for those ready to have an extra level of fun.
Great stuff.
Title: Re: Flyer Movement Framework
Post by: Clean0nion on January 31, 2014, 11:05:41 AM
Here's what I'd end up doing with it, most likely. And this is based on V's recent ideas.
A spore. An orange spore. When it lands, it releases cataclysmic amounts of doom and creeper. However, land an ore mine on the spore, and you can mine it down.
So what I'm basically saying is when you get a spore that's too big for a beam to handle, you mine it out instead.
Title: Re: Flyer Movement Framework
Post by: Grayzzur on January 31, 2014, 11:16:48 AM
Very cool. I'll definitely be examining your code for some ideas.

Say, what's up with the 3rd landing on the demo? It kind of does a diagonal landing across both strips. (From your given demo's start, watch from the 2nd takeoff to the 3rd landing.)
Title: Re: Flyer Movement Framework
Post by: eduran on January 31, 2014, 12:01:16 PM
Quote from: Grayzzur on January 31, 2014, 11:16:48 AM
Very cool. I'll definitely be examining your code for some ideas.
Right now there are barely any comments, which makes some parts almost impossible to understand. The next iteration will have a lot more, so you may want to wait for that version.

Quote from: Grayzzur on January 31, 2014, 11:16:48 AM
Say, what's up with the 3rd landing on the demo? It kind of does a diagonal landing across both strips. (From your given demo's start, watch from the 2nd takeoff to the 3rd landing.)

That's a bug/missing feature. Given a non-zero turn radius, there are points that you cannot reach from the current location without going somewhere else first. My code checks each new waypoint to make sure it can be reached. If that's not possible, it just ignores the waypoint and skips to the next one.
That happens with the landing you pointed out: the approach point can't be reached, so it is skipped and the flyer goes for the next waypoint, at the end of the runway.
Ideally, the script should insert one additional waypoint (one from where the approach point can be reached) instead of simply ignoring the point. I'll have to think about how to best do that.

My current to-do list:
- basic takeoff and landing animations
- commenting the code
- fix the bug mentioned above
- think about moving targets and how to best deal with these
Title: Re: Flyer Movement Framework
Post by: Annonymus on February 01, 2014, 08:38:43 AM
May I suggest (to Clean0nion) that you produce an AoO mass effect on the landing of the orange spore? That would seem even more destructive without doing irreparable damage. (As long as you have terps, else it will be just some more motivation to not let the spore land)
Title: Re: Flyer Movement Framework
Post by: eduran on February 01, 2014, 12:30:47 PM
Updated the opening post with a new version of the demo map and the script. Changes:
Title: Re: Flyer Movement Framework
Post by: eduran on February 02, 2014, 09:32:38 AM
Turns out that adding takeoff and landing animation causes a new problem.
Here is what the animation looks like:
Spoiler

(http://knucklecracker.com/forums/index.php?action=dlattach;topic=15387.0;attach=17060;image)
[close]

And here is what happens when Beams come into play:
(http://knucklecracker.com/forums/index.php?action=dlattach;topic=15387.0;attach=17058;image)

The issues is that I am only moving the image up when the flyer is in the air, not the CRPL core itself. That is a easy to do and does not affect the movement code. Changing the script so that it could deal with an off-center core would require a lot of extra work.
So, does anyone have an idea for a takeoff/landing animation that keeps the flyer image centered on the core? Or a different idea of how I could deal with this?
Title: Re: Flyer Movement Framework
Post by: Grayzzur on February 02, 2014, 12:23:12 PM
You could take a peek at my flyer code for CW1 Drones. Uncomment the pieces in the CW1_Drone.crpl code for the image called "point" (3 lines, two in the once block and one near the bottom of the main routine), and you'll see a circle appear below the drone representing it's ground position.

It's not well commented yet for public consumption, but basically I'm tracking position with variables GroundX and GroundY. I do my flying/navigation calculations based off that, then adjust the CRPL Core based off that point and it's current "height" (then calculate the shadow position from there).

http://knucklecracker.com/forums/index.php?topic=14650.0 (http://knucklecracker.com/forums/index.php?topic=14650.0)

I ran into the same issue when I was dealing with CW1-style spores. My missiles were tracking a point well below my spore cores.
Title: Re: Flyer Movement Framework
Post by: Michionlion on February 02, 2014, 01:40:36 PM
Here's an idea:  what about tying an invisible but targetable tower to the image, and making the main tower untargetable, then killing both when the first gets destroyed?
Title: Re: Flyer Movement Framework
Post by: eduran on February 02, 2014, 01:57:58 PM
Quote from: Grayzzur on February 02, 2014, 12:23:12 PM
You could take a peek at my flyer code for CW1 Drones.
That has been very helpful, thank you. Also a lot easier to implement than I feared.

Quote from: Michionlion on February 02, 2014, 01:40:36 PM
Here's an idea:  what about tying an invisible but targetable tower to the image, and making the main tower untargetable, then killing both when the first gets destroyed?
That a good backup plan, in case I fail to get Grayzzur's suggestion to work.
Title: Re: Flyer Movement Framework
Post by: knucracker on February 02, 2014, 02:42:47 PM
Hold your horses... you are actually reporting a bug and don't know it:)

There are  two Commands SetTargetOffsetX and SetTargetOffsetY...  that are pixel offsets from the center of a core that things are supposed to shoot at.  Snipers will obey those offsets, but I forgot all about that when I added beam support the other day.  So in the next build I will make Beams pay attention to those offsets.

For instance if you take a look at Harvester.crpl in the Farbor story mission you'll find this function

:RefreshImageAltitude
SetImagePositionY(Self "main" <-altitude mul (<-altitudeDisplacement))
<-baseScale add(<-altitude mul (<-altitudeScale)) ->scale
SetImageScale(Self "Main" <-scale <-scale)

SetImagePositionX(Self "shadow" <-altitude mul (<-altitudeDisplacement) div(2))
1 sub (<-altitude mul (0.7)) mul (255) ->alpha
SetImageColor(Self "shadow" 255 255 255 <-alpha)

SetTargetOffsetY(<-altitude mul (<-altitudeDisplacement))


Here I move the harvesters up the y axis to simulate takeoff, and so I also change the targetoffset.  This makes the sniper tracer line point to the harvester image.  You could also use this for more exotic effects like a boss enemy that has several parts that have to be destroyed.  Snipers 'know' which ones to shoot out in sequence to take out all of the parts.

Anyway, I'll go make beams obey this offset now...
Title: Re: Flyer Movement Framework
Post by: eduran on February 02, 2014, 02:47:30 PM
I didn't even know about these commands, they are exactly what I need  :)
Title: Re: Flyer Movement Framework
Post by: knucracker on February 02, 2014, 03:03:51 PM
You can imagine how they came to be...
I did the Harvesters when I made Farbor, then slapped my forehead when I saw the snipers shoot at the ground under the harvesters.  So I followed the same path as you.

I wasn't as ingenious as Grayzzur so I went and added offset support into the only weapon where it mattered at the time (the Sniper).  I've now just finished adding it to beams (and even tested it briefly as a bonus:) )

But if you are doing something like a missile as Grayzzur mentioned, then you'd have to take matters into your own hands.  It sure would be nice if you could get query the missile's target for the targetOffset, though.  If I had made SetTargetOffsetX and Y take a unit ID as the first argument you could do that...

So, I just added these commands for the next build.
GETUNITTARGETOFFSETX, GETUNITTARGETOFFSETY, SETUNITTARGETOFFSETX, SETUNITTARGETOFFSETY

They work like this:
GetUnitTargetOffsetX(<-unitUID) ->tx
SetUnitTargetOffsetX(<-unitUID 5)

In other words they take a unitUID as a first argument whereas the existing APIs always assume 'Self'.  With this you could make some other core like a missile that can query its target offsets for where to fly to.

(Yes, I know I should have made the original APIs take a unit UID... but I must have been having a rushed day when I did the original APIs)

Title: Re: Flyer Movement Framework
Post by: Grayzzur on February 02, 2014, 04:14:27 PM
And the lightbulb for what SetTargetOffsetX/Y does just went off. I always wondered what the heck those were for.
Great! Now I have to go rewrite everything...

;D

I can imagine how they came to be.
I did the Harvesterscustom spores when I made FarborCW1 SAMs, then slapped my forehead when I saw the snipersmissiles shoot at the ground under the harvestersspores. Lacking the ability to add features directly to the game, I came up with my plan. I do get a warm and fuzzy hearing Virgil call it ingenious, so thank you for that.
Title: Re: Flyer Movement Framework
Post by: eduran on February 02, 2014, 04:17:31 PM
Quote from: Grayzzur on February 02, 2014, 04:14:27 PM
Lacking the ability to add features directly to the game, I came up with my plan.
Lacking the ability to add features directly to the game, I came to the forum to cry about it :) Which turned out to be almost as good.
Title: Re: Flyer Movement Framework
Post by: eduran on February 03, 2014, 06:33:28 PM
Another update:
I am currently using my movement script as part of a map. Doing that allowed me to identify a couple of flaws in the design, but most of them were easily fixable. On the upside, not having to worry about movement makes implementing new enemies a lot easier. I am even using it to drive a ship.
Once the map is released I will give the code another once-over and after that it should be ready for release.


Some details on how I tackled the 'approach a cell at a certain angle' problem. Feel free to ignore this part, it's mostly meant as a way to sort out my thoughts on the subject.

1) Fixed turn radius. All of the calculations hinge on that one. Variable speed and acceleration are not a problem and neither is changing turn radius in between moves. But during a move, the turn radius is fixed. That also means the flyer is either turning as hard as possible or not at all (i.e. flying in a straight line).

2) Two turns are enough to reach any destination from any origin. To get the shortest possible path, the first turn has to happen immediately, followed by going straight. The second turn ends at the destination, at just the right angle.

Here's a screenshot of a flyer plotting a path. The CRPL cores, lines, circles are all generated by the script (I gave up on debugging with just the trace log pretty quickly).
Spoiler
(http://knucklecracker.com/forums/index.php?action=dlattach;topic=15387.0;attach=17071;image)
[close]
The destination is the black arrow at the top left, the origin is the flyer itself. The steps I outlined above (turn, straight line, turn) allow for four different pathes: turning either left or right at both the start and the end. That's why there are four colored lines.
I was unable to come up with a way to figure out which path is the shortest, so the script resorts to brute force: it calculates the length of all of them and selects the shortest one.

3) Once that is done, a number of parameters are fed into a function that converts distance traveled along the selected path into a set of coordinates and a direction. At that point, the actual movement is as simple as
<-coveredDistance add(<-currentSpeed) ->coveredDistance
@Distance2Position(<-coveredDistance) ->myDir ->myY ->myX
@RotateImages(<-myDir)
SetUnitAttribute(Self CONST_PIXELCOORDX <-myX)
SetUnitAttribute(Self CONST_PIXELCOORDY <-myY)


Things that work very well using this method:
   
The downside is that a lot of calculations are necessary. So far I didn't notice any performance issues with up to 10 moving units, but using a lot of them or changing targets often (e.g. when you are tracking a moving target) could be a problem.
Title: Re: Flyer Movement Framework
Post by: knucracker on February 03, 2014, 07:05:23 PM
That's very, very clever.  Back before CW1 I worked on two game prototypes that I never went anywhere with.  One of them was a stop motion space combat sim (similar to the way Space Rangers works).  The motion of the spacecraft in that prototype was very similar to the way this works out.

I have a feeling this will get used by a lot of people in a lot of maps... shoot, I might even use it if I can find the time to make the map I've been wanting to make.
Title: Re: Flyer Movement Framework
Post by: Clean0nion on February 04, 2014, 03:02:29 AM
Quote from: virgilw on February 03, 2014, 07:05:23 PM
That's very, very clever.  Back before CW1 I worked on two game prototypes that I never went anywhere with.  One of them was a stop motion space combat sim (similar to the way Space Rangers works).  The motion of the spacecraft in that prototype was very similar to the way this works out.

I have a feeling this will get used by a lot of people in a lot of maps... shoot, I might even use it if I can find the time to make the map I've been wanting to make.
You know what? I'm deleting Sall 5 right now. Eduran, get prepared for some credit coming your way.
Title: Re: Flyer Movement Framework
Post by: Cavemaniac on February 04, 2014, 03:30:08 AM
Quote from: virgilw on February 03, 2014, 07:05:23 PM
That's very, very clever.  Back before CW1 I worked on two game prototypes that I never went anywhere with.  One of them was a stop motion space combat sim (similar to the way Space Rangers works).  The motion of the spacecraft in that prototype was very similar to the way this works out.

I have a feeling this will get used by a lot of people in a lot of maps... shoot, I might even use it if I can find the time to make the map I've been wanting to make.

I've said it before, I'll say it again, the most amazing stop motion/'turn based' space homing missile game I've ever seen is Critical mass.

http://www.windowsgames.co.uk/critical.html

Looks a bit crappy, gameplay is amazing.

The turning radius is a massively important part of the game.

After the CW series, this game has soaked up more of my life than any other...

Visit and be inspired - free demo, but it's a bit limited.
Title: Re: Flyer Movement Framework
Post by: thepenguin on February 06, 2014, 03:59:44 PM
Quote from: eduran on February 03, 2014, 06:33:28 PM
Another update:
I am currently using my movement script as part of a map. Doing that allowed me to identify a couple of flaws in the design, but most of them were easily fixable. On the upside, not having to worry about movement makes implementing new enemies a lot easier. I am even using it to drive a ship.
Once the map is released I will give the code another once-over and after that it should be ready for release.


Some details on how I tackled the 'approach a cell at a certain angle' problem. Feel free to ignore this part, it's mostly meant as a way to sort out my thoughts on the subject.

1) Fixed turn radius. All of the calculations hinge on that one. Variable speed and acceleration are not a problem and neither is changing turn radius in between moves. But during a move, the turn radius is fixed. That also means the flyer is either turning as hard as possible or not at all (i.e. flying in a straight line).

2) Two turns are enough to reach any destination from any origin. To get the shortest possible path, the first turn has to happen immediately, followed by going straight. The second turn ends at the destination, at just the right angle.

Here's a screenshot of a flyer plotting a path. The CRPL cores, lines, circles are all generated by the script (I gave up on debugging with just the trace log pretty quickly).
Spoiler
(http://knucklecracker.com/forums/index.php?action=dlattach;topic=15387.0;attach=17071;image)
[close]
The destination is the black arrow at the top left, the origin is the flyer itself. The steps I outlined above (turn, straight line, turn) allow for four different pathes: turning either left or right at both the start and the end. That's why there are four colored lines.
I was unable to come up with a way to figure out which path is the shortest, so the script resorts to brute force: it calculates the length of all of them and selects the shortest one.

3) Once that is done, a number of parameters are fed into a function that converts distance traveled along the selected path into a set of coordinates and a direction. At that point, the actual movement is as simple as
<-coveredDistance add(<-currentSpeed) ->coveredDistance
@Distance2Position(<-coveredDistance) ->myDir ->myY ->myX
@RotateImages(<-myDir)
SetUnitAttribute(Self CONST_PIXELCOORDX <-myX)
SetUnitAttribute(Self CONST_PIXELCOORDY <-myY)


Things that work very well using this method:

  • arriving at the target at exactly the intended angle
  • using the shortest path to do so (unless someone proves me wrong and comes up with a shorter path)
  • the movement looks very smooth, even at high speed
   
The downside is that a lot of calculations are necessary. So far I didn't notice any performance issues with up to 10 moving units, but using a lot of them or changing targets often (e.g. when you are tracking a moving target) could be a problem.

This is roughly the space shuttle landing system... wow.  Awesome job!