Flyer Movement Framework

Started by eduran, January 31, 2014, 09:26:39 AM

Previous topic - Next topic

eduran

I am starting this thread to continue the discussion about flying units started here.

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.

knucracker

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.

Helper

"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.

Clean0nion

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.

Grayzzur

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.)
"Fate. It protects fools, little children, and ships named 'Enterprise.'" -William T. Riker

eduran

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

Annonymus

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)
If a topic started by me is in the wrong place feel free to move it at anytime.

eduran

Updated the opening post with a new version of the demo map and the script. Changes:

  • Takeoffs and landings are now supported. The animation mimics that of Strafers (image increases in size and moves up, shadow decreases in size and moves to the left). Added @QueueTakeOff and @QueueLanding functions to the example script. They work during motion (landing on/starting from a runway) and while hovering (the way Strafers start/land).
  • Code is more structured and has additional comments. Should be easier to read now, but there are still a lot of explanations missing.
  • Updated the example script to include landings and takeoffs.
  • Waypoints that are too close to the flyer to be approached directly are no longer ignored. The flyer will now loop around to get to these waypoints.
  • The code to make the flyer approach a waypoint at the correct angle has been rewritten and is a lot more robust now.

eduran

#8
Turns out that adding takeoff and landing animation causes a new problem.
Here is what the animation looks like:
Spoiler

[close]

And here is what happens when Beams come into play:


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?

Grayzzur

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

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.
"Fate. It protects fools, little children, and ships named 'Enterprise.'" -William T. Riker

Michionlion

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?
"Remember kids, the only difference between science and messing around is writing it down."
                                                                                                                         - Adam Savage

My website
My CW1, and CW2 maps!

eduran

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.

knucracker

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...

eduran

I didn't even know about these commands, they are exactly what I need  :)

knucracker

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)