Jump to content

Modifying Path-finding and Unit Motion Logic


Recommended Posts

Hi All,

So I've recently downloaded the Pyrogenesis Engine after scouting around for various Open Source RTS engines to try and develop a game on. I'm trying to implement this engine for a 3D RTS in the likes of Homeworld, but I'm realizing that the Pathfinding and unit motion is dependent on a 2D Plane, and it seems like the only thing that affects a unit's position in Z-space (or I guess in this engine Y is the height) is basically the terrain mapping.

Has anyone dug deep enough into Pathfinding to help me out and point me to where to start? I'f I'm trying to completely re-do Path Finding to allow freedom of motion in the height dimension, where do I start? Am I right that the height of a unit's path is designated by the terrain?

I don't need anyone to do my work for me, as it would ruin the learning experience. I just want to know how the Unit Motion controls flow in relation to Pathfinding and Terrain?

Cheers!

Found some decent hints on ICmpPosition.cpp

/**

* Represents an entity's position in the world (plus its orientation).
*
* Entity positions are determined by the following:
* - X, Z coordinates (giving left/right and front/back coordinates on the map)
* - Y offset (height; entities always snap to the ground, then are offset by this amount)
* - 'Floating' flag (snap to surface of water instead of going underneath)
* As far as the simulation code is concerned, movements are instantaneous.
* The only exception is GetInterpolatedTransform, used for rendering, which may
* interpolate between the previous and current positions.
* (The "Jump" methods circumvent the interpolation, and should be used whenever immediate
* movement is needed.)
*
* Orientations consist of the following:
* - Rotation around upwards 'Y' axis (the common form of rotation)
* - Terrain conformance mode, one of:
* - Upright (upwards axis is always the world Y axis, e.g. for humans)
* - Pitch (rotates backwards and forwards to match the terrain, e.g. for horses)
* - Pitch-Roll (rotates in all directions to match the terrain, e.g. for carts)
* - Roll (rotates sideways to match the terrain)
* NOTE: terrain conformance is currently only a local, visual effect; it doesn't change
* the network synchronized rotation or the data returned by GetRotation
* - Rotation around relative X (pitch), Z (roll) axes (rare; used for special effects)
* NOTE: if XZ rotation is non-zero, it will override the terrain conformance mode
*
* Entities can also be 'outside the world' (e.g. hidden inside a building), in which
* case they have no position. Callers <b>must</b> check the entity is in the world, before
* querying its position.
*/
Edited by Valvador
  • Like 1
Link to comment
Share on other sites

The height of units is controlled by cmpPosition. Basically, UnitMotion changes the 2D coordinates of an entity, and then registers it in the Position component. When you then query the 3D position of the unit, the height is automatically calculated from information s.a the height offset, the terrain height and the water plain height.

This position it's then used to render the entity.

In the scripts, there is a UnitMotionFlying component that allows planes to change height. Though that component is used without pathfinder, and it's just a test.

  • Like 2
Link to comment
Share on other sites

Thank you for that information. Despite the good level of documentation that I've seen in the code, this information should speed up my research/modification process.

My current goal is to make one of the existing OAD units fly on command, ignoring the terrain "clamp".

So from further research based on your information I was able to figure out the following points:

  1. CCmpPosition::MoveTo() and MoveToAndRotate() handle semi-instantaneous motions. While they are interpolated, I assume they are used once per frame or whatever the tick events are.
  2. Pathfinding and UnitMotion classes feed incremented X and Z coordinates that are utilized as arguments for MoveToAndRotate() statements for motion, meaning I would have to implement some kind of Y-motion logic into Pathfinding and UnitMotion, which would require writing a lot of new obstacle and abstraction classes. (As expected)
  3. Moving a unit by a click ignores the Y-Offset and instead uses the Terrain directly UNDER the unit as a motion target. This means that Mouse-click movement orders have an association with the Terrain that I will have to remove as well (Shouldn't be too hard, can always use a base-plane as the target and raise it based on the selections elevation).

On thing I am unclear about is how the "Command Flow" works between scripting and C++ actions. For example, I notice that commands are given through Scripts which makes it difficult to judge when trying to analyze a call stack. So far my guess at what happens is the following:

  • Scripting Interface picks up mouse-click order to move a unit.
  • Pathfinding Algorithm creates path and feed Smalls Incremented motion position to Unit Motion
  • UnitMotion (with the help of Pathfinding) feeds basic "MoveTo" increments to the Position class which interpolates motion over small time periods.

Is this correct? This means that if I want to be able to give a unit a move order to X, Z, AND Y I will have to either create or expand the Pathfinding, UnitMotion classes and make sure to properly Register them and bind them on a Script-level, so that mouse commands actually activate these new uses?

Also, does every entity have it's own UnitMotion cass? I'm trying to figure out how CCmpUnitMotion::Move(dt) only has a single argument, and it's dt. What references it back to whatever object is moving?

Overall, I gotta say your code is easier to read then what I have to deal with at work, so thanks for that!

Cheers.

Edited by Valvador
Link to comment
Share on other sites

The game has a tick every turn, currently, that's every 200ms (though it may become shorter in the future), when there's lag, the turns become longer. Every simulation change can only happen on a turn (some components listen to a turn tick, execute some action, and notify other components if necessary).

Between the turns, there's also graphical interpolation, to make the image smoother. Everything that happens between turns isn't synced in any way between different players.

F.e. player commands in a network game need to wait until the next turn to be executed.

The UnitMotion and Pathfinder only really care about simulation. Every turn, the UnitMotion component brings the Position a bit closer to the next waypoint given by the Pathfinder. That's what happens in the move command. The dt argument is the length of the turn, so the UnitMotion component can know how far it can move. The Position component does care about graphics, so with a MoveTo command, only the next position is set, and every frame is interpolated towards that new position.

The move command are actually given by the scripted UnitAI. That's quite a big piece of game logic that decides stuff like detecting enemies to approach and fight, finding a new tree when the previous is cut down, walking in formation... The move command are given directly to UnitMotion.

About the components, you are about right. Every entity is defined by an xml template, that denotes the entity has an instance of certain components, with certain settings (like the move speed in UnitMotion). There are a few special entities (like the SYSTEM_ENTITY and player entities), but most components are used on regular entities. The components are referred to with an ID on the scripted side (like IID_UnitMotion). It's also possible to have separate implementations for the same interface, and refer to them with the same I'd. Like the scripted component UnitMotionFlying is accessible under the IID_UnitMotion, so UnitAI sees no difference, and you can command planes like any other unit.

Link to comment
Share on other sites

  • 2 weeks later...

Hey Sanderd,

So after looking into the information you provided me I attempted to implement a new interface Class. I added the following files: ICmp3DUnitMotion.h, ICmp3DUnitMotion.cpp, CCmp3DUnitMotion.cpp. I've implemented these files and used Dummy methods that do pretty much nothing in the mean time. My plan would be to introduce a new class of units that would use these 3D Motions after implementing the 3D Pathfinding Algorithm. But in the mean time I want to implement it without any function along side the normal UnitMotion, so that I know I'm not ruining any existing functionality.

The issue I am having now is that I finally got it to compile by using the following interface connections:

ICmp3DUnitMotion.h - DECLARE_INTERFACE_TYPE(3DUnitMotion)

ICmp3DUnitMotion.cpp - DEFAULT_SCRIPT_WRAPPER(3DUnitMotionScripted) (inside the class context), REGISTER_COMPONENT_SCRIPT_WRAPPER(3DUnitMotionScripted) (Outside the class context)

CCmp3DUnitMotion.cpp - DEFAULT_COMPONENT_ALLOCATOR(3DUnitMotion) (Inside the class context after the ClassInit), REGISTER_COMPONENT_TYPE(3DUnitMotion) (Outside the class context)

The compilation works fine with my dummy methods that do nothing but overwrite virtual functions in my CCmp3DUnitMotion.cpp file. However when I launch the game I get a bunch of incoherent JavaScript errors on the screen and new games do not launch. When I tried to load a saved game I got a crash that takes me out to debug_warn(L"CCmpDecay must not be used on non-local (network-synchronised) entities"); inside of CCmpDecay.cpp.

Is there something in the JavaScript end of the game that tries to implement and test all the C++ Components that were registered? Could this be causing failures in the implementation of my dummy class? I thought since no units even know of the existence of a 3DUnitMotion interface, there should be no reason for JavaScript to even enter my new dummy class. Is there something that tries to run through EVERY registered class in JavaScript to make sure that the C++ functionality works?

it4WJMI.png

Just noticed the above error. Maybe I don't understand whats going on in the JavaScript side. Where is all the JavaScript code? How is it implemented? Isn't it just an interface called by unit specific script commands that is ultimately tied to C++ commands?

Edited by Valvador
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...