• Advertisement
Sign in to follow this  

The Player class problem

This topic is 3344 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello! In many of my game projects, I end up having a quite monolithic "Player" class. The thing is, in a game, the player does have a lot of possible behavior: if I take the example of a Super Mario Bros. clone, the player class would have to handle walking, running, jumping, shooting and swimming mechanics, to name a few. Is there a way to split up such behavior into separate classes in order to make the code more manageable and maybe facilitate code reuse for NPCs? I'm open for suggestion for entity class hierarchy and component-based entity models, or anything in-between. Thanks!

Share this post


Link to post
Share on other sites
Advertisement
Let's consider movement. Your player object is composed of several parts:
  • The model data. This is usually a position and a velocity, a collision rectangle, and perhaps also an "is touching floor" boolean or a "duration airborne" number. This is certainly not a player-only set of attributes, so you can move it out of the class.
  • The model evolution rules. This is how the model evolves over time. This usually involves applying gravity, integrating acceleration into velocity and velocity into position, and perhaps increasing the "duration airborne" value. This doesn't have to be part of the player object.
  • The model interaction rules. This is how the model reacts to world events, such as collisions. For instance, colliding with an object moves the player back and in some conditions resets the "touching floor" and "duration airborne" values. This can also be moved out.
  • The model control rules. This how external considerations (player input, AI) affect the model. For instance, "move left" and "move right" change horizontal velocity, while "jump" sets vertical velocity and the "is touching floor" property. Keys may have different effects depending on the duration airborne.


So, except for the model control rules, there's nothing player-specific in there. So, create a "MovingObject" which handles the data and model evolution rules, then add to it filters to handle the interaction rules, and give the player object an instance of MovingObject and the corresponding filter.

You can then have another object to handle the shooting.

Share this post


Link to post
Share on other sites
ToohrVyk,

Thanks for your ideas, it seems to be a very good way to split up data and behavior. Let me reformulate the four movement components you talked about, just to see if I get it right:


  • Data: No behavior, simply positional and velocity values, a collision shape and possibly some extra data.

  • Evolution: Transformation of the movement over time based on the physical properties of the environment (you fall more slowly in water). Does not change the position, simply the velocity.

  • Interaction: Changes the position based on the velocity and obstacles. Dispatches collision messages when they occur.

  • Control: Changes the data based on external actions (user input or AI decisions). This is entity-dependant, and the player's movement controller still handles walking, running, jumping, shooting and swimming, although at a lesser degree. Or do you have different model control rules for the different actions?



Here's an attempt at an implementation in pseudo-C#, is that similar enough to how you'd implement it?

Entity player = new Entity();
player.AddComponent(new MovementData() { Position = ..., BoundingRect = ... });
player.AddComponent(new MovementEvolution() { Gravity = ..., AirFriction = ... });
player.AddComponent(new MovementInteraction());
player.AddComponent(new PlayerControlRules());

void MovementEvolution.Update()
{
GetComponent<MovementData>().Velocity += Gravity;
}

void MovementInteraction.Update()
{
MovementData data = GetComponent<MovementData>();
if(IntersectsOtherEntities(data.shape, data.velocity)
FireIntersectedEvent(intersectedEntities);
else
data.Position += data.Velocity;
}

void PlayerControlRules.Init()
{
AttachToEvent(GetComponent<MovementInteraction>().IntersectionEvent);
}

void PlayerControlRules.Jump()
{
MovementData data = GetComponent<MovementData>();
if(data.IsLanded)
{
data.IsLanded = false;
data.Velocity.Y = ...
}
}



Sorry for the big post, I just want to be sure I understand as much as I can what you suggested.

Share this post


Link to post
Share on other sites
Quote:
Original post by Trillian
  • Evolution: Transformation of the movement over time based on the physical properties of the environment (you fall more slowly in water). Does not change the position, simply the velocity.

  • Nope. If it involves the environment, then it's interaction. Of course, if you want to have differing amounts of friction, you should probably give the object a "friction" property (so that it doesn't have to read that property from the environment when it computes the evolution). Then, you'd have an interaction function set the appropriate friction when the environment changes. This also changes the position: this is really about the object moving where it should move according to its own constraints.

    Quote:
  • Interaction: Changes the position based on the velocity and obstacles. Dispatches collision messages when they occur.
  • Movement in itself (and integration) is ideally done in an internal manner. Once you know where that movement would end up, and you notice that this would involve a collision, you undo the movement and apply interaction-related functions to the data.

    Quote:
  • Control: Changes the data based on external actions (user input or AI decisions). This is entity-dependant, and the player's movement controller still handles walking, running, jumping, shooting and swimming, although at a lesser degree. Or do you have different model control rules for the different actions?
  • I would let the controller act differently based on what's going on. For instance, if the player is trying to move up, then:
    • Underwater, swim up.
    • On the ground, jump up.
    • Airborne, move up until airborne for a certain duration.


    As for the code, I'd probably have it look a little bit more like:


    public Handle AddPlayerToWorld(PlayerControls controls)
    {
    PhysicalObject obj = new PhysicalObject(); // Stores pos/vel/acc

    Handle removalHandle = new Handle(); // So we can remove the object

    removalHandle += this.updater.Add(new PhysicalObjectUpdater(obj));
    removalHandle += this.updater.Add(new PlayerController(obj, controls));
    removalHandle += this.collisions.Add(new PhysicalObjectCollisionHandler(obj));
    removalHandle += this.rendering.Add(new PlayerRenderer(obj));

    return removalHandle;
    }

    Share this post


    Link to post
    Share on other sites
    Hey ToohrVyk, thanks again for your reply, you're really helping me out!

    The difference between evolution and interaction is still a little foggy to me. If friction has to be part of interaction, and maybe gravity as well (imagining a game where gravity could change), then what is its purpose? Now, in your last post, you said that evolution changes the velocity by the acceleration (which is in the data, I guess?) and the position by the velocity. But, as evolution has no knowledge of the environment, it could move the object into walls, which is unintended. In order for it not to move object into walls, interaction would have to always run first to ensure that the velocity will not be enough to allow evolution to move it into obstacles. That's why I don't see the benefit of splitting evolution and interaction, I can't understand how they could work in isolation. Maybe you could post a few samples of tasks best handled by evolution?

    BTW, what I said assumes a predictive collision detection model (detect that the object will not collide before moving them), which I prefer.

    As for your sample code, I don't see how you're splitting evolution and interaction. Does the PhysicalObjectUpdater handle both? And is the PhysicalObjectUpdater responsible for submitting intersection events to the collision manager?

    You have given me much to think about, and I thank you for that :)

    Share this post


    Link to post
    Share on other sites
    Quote:
    Original post by Trillian
    The difference between evolution and interaction is still a little foggy to me. If friction has to be part of interaction, and maybe gravity as well (imagining a game where gravity could change), then what is its purpose? Now, in your last post, you said that evolution changes the velocity by the acceleration (which is in the data, I guess?) and the position by the velocity. But, as evolution has no knowledge of the environment, it could move the object into walls, which is unintended. In order for it not to move object into walls, interaction would have to always run first to ensure that the velocity will not be enough to allow evolution to move it into obstacles. That's why I don't see the benefit of splitting evolution and interaction, I can't understand how they could work in isolation. Maybe you could post a few samples of tasks best handled by evolution?


    The question here is, can I compute this with only my internal model data? If only internal data is required, it's evolution, if external data is required, it's interaction.

    What you place in the "interaction" layer is up to you. I tend to build complex data structures that can evolve on their own and be "corrected" by their environment if they make mistakes (such as overlapping other objects), but you can choose to make all physical integration part of "interaction" instead of "evolution" if you want to. And it could be useful if you're using collision prediction, too. Just avoid splitting it over both interaction and evolution.


    Quote:
    As for your sample code, I don't see how you're splitting evolution and interaction. Does the PhysicalObjectUpdater handle both? And is the PhysicalObjectUpdater responsible for submitting intersection events to the collision manager?
    No, I'd let the collision manager detect collisions and apply the events to the objects. PhysicalObjectUpdater is evolution, PhysicalObjectCollisionHandler is interaction (you can collide with air or water : you don't bounce, you just change your friction coefficient).

    Share this post


    Link to post
    Share on other sites
    Thanks a lot for all of your feedback, it was great discussing with you. I'm going to try to implement a system with a structure similar to what we talked about. I hope it will help me built a more modular and reusable entity system.

    Thanks again!

    Share this post


    Link to post
    Share on other sites
    Sign in to follow this  

    • Advertisement