Player Movements in 3D & SunBurn Components

The component system in SunBurn is really quite an amazing concept and opens up a huge array of opportunity for the engine. Components can be added to Scene Objects; they are called every update and allow you to apply game logic directly to an object that you have added to the editor without needing to identify the object in the code and add corresponding logic.

Simple Component

Starting with a simple example I created a simple component which can be added to a Scene Object and will cause the Scene Object to start spinning slowly on the Z Axis.

[Serializable]
public class SpinZSlowComponent : BaseComponentAutoSerialization<ISceneEntity>
{
    public override void OnUpdate(GameTime gameTime)
    {
        // Calculate the elapsed rotation based on the time. float angle = (float)gameTime.ElapsedGameTime.TotalSeconds * 0.3f;
        Matrix rotation = Matrix.CreateRotationZ(angle);

        // Apply the rotation to the parent object (owner of the component). ParentObject.World = rotation * ParentObject.World;
    }
}

Once the Component has been defined in the code, open up the editor and then right click on the Object, Chose Add Component to Objects and then select the newly defined component.

image

The object in the editor immediately starts executing the new game logic; obligatory static screen shots of a cube rotating; doesn’t have the same effect as video but this is just the introduction.

imageimageimage

A rotating cube is interesting and all but considering that the code for this is really just 3 lines, we haven’t saved all that much time (unless your whole scene is going to be slowly rotating cubes, in which case we have just saved you a little bit more). What is useful though is this component can just be dropped in to the your SunBurn project and used straight away, nothing else needs to be setup or known.

Taking this a bit further; games need a method of handling player input, in a 3D game that player input will need to interact with an object (the player) – what a great opportunity for a component.

Player Component

This component demonstrates how to take player input, via the keyboard and then create the relative updates and rotations for the player. I, J, K, L are mapped to Forward, Left, Backwards and Right respectively. U & O are mapped to rotations, Left and Right respectively. A single variable, forceToApply is looking up an external value, which we will discuss shortly but you can put a direct value in here if you want as well.

[Serializable]
public class PlayerMovement_Keyboard_Editor_Component : BaseComponentAutoSerialization<ISceneEntity>
{
    public override void OnUpdate(GameTime gameTime)
    {
        KeyboardState keyboard = Keyboard.GetState();

        // Cast the Object the component is attached to, to a scene object SceneObject sceneobject = (SceneObject)ParentObject;

        // Create a new set of vectors to store the movements Vector3 additionalForce = new Vector3();

        float forceToApply = PassThrough.PlayerForce;

        // Move Forward if (keyboard.IsKeyDown(Keys.I))
        {
            additionalForce.Z = -forceToApply;
        }
        // Move Backwards if (keyboard.IsKeyDown(Keys.K))
        {
            additionalForce.Z = forceToApply;
        }
        // Slide Left if (keyboard.IsKeyDown(Keys.J))
        {
            additionalForce.X = -forceToApply;
        }
        // Slide Right if (keyboard.IsKeyDown(Keys.L))
        {
            additionalForce.X = forceToApply;
        }
        // Rotate to the Left if (keyboard.IsKeyDown(Keys.U))
        {
            float angle = (float)gameTime.ElapsedGameTime.TotalSeconds * forceToApply;
            Matrix rotation = Matrix.CreateRotationY(angle);
            sceneobject.World = rotation * sceneobject.World;
        }
        // Rotate to the Right if (keyboard.IsKeyDown(Keys.O))
        {
            float angle = (float)gameTime.ElapsedGameTime.TotalSeconds * -forceToApply;
            Matrix rotation = Matrix.CreateRotationY(angle);
            sceneobject.World = rotation * sceneobject.World;

        }
        // Defy Gravity - Float Upwards if (keyboard.IsKeyDown(Keys.Space))
        {
            additionalForce.Y = forceToApply;
        }

        // Apply the new force to the Object that the component is attached to sceneobject.CollisionMove.ApplyObjectForce(additionalForce);
    }
}

Adding this component to an object in the editor allows the object to immediately receive keyboard input and move around the world space. That’s a reusable component for sure.

During testing of this component I found that depending on the scale I was working with I got mixed results due to the way that objects slide along surfaces, the bigger the object the less force I needed to apply (I could have used the materials and dropped down the Friction on the materials..) to assist this testing, without needing to modify every other object in the scene, we can use a Custom Scene Entity to provide input to modify the variable without needing to go back to the component code and edit directly.

A Short Cut

That variable I mentioned earlier, it was referring to a static variable so that it can be updated outside of the component. I’m sure there is a more elegant solution to this, however I’m not actually sure what that solution is Smile

public static class PassThrough {
    // This static variable is used to marshal the values between the Custom Editor SceneEntity and the Component // It also holds the default value, so if the component is added without the Custom Editor SceneEntity it will still do something. public static float PlayerForce = 0.5f;
}

Custom Editor Scene Entity

Enabling a variable to be updated during run time can be achieved through the use of a Editor Created Object, it’s derived from Scene Entity and once added to a scene through the editor it can then be used to update variables. Using this process, when the scene object is updated, we can update the related static variable and change the amount of force that is applied with the player movements.

image

 

 

 

 

 

 

 

The Editor Created Object is also [Serializable] so the updates through the UI will be persisted between the various edits and runs.

[Serializable]
[EditorCreatedObject]
public class PlayerForce_Entity : SceneEntity {
    private float _PlayerForce = 0.5f;

    [EditorProperty(true, Description = "PlayerForce", ToolTipText = "This is the amount of force to use when the player is moving. Only add one to the scene")]
    public float PlayerForce {
        get { return _PlayerForce; }
        set { _PlayerForce = value; PassThrough.PlayerForce = value; }
    }

    public override void SetObjectData(SerializationInfo info, StreamingContext c)
    {
        base.SetObjectData(info, c);
        SerializationHelper.DeserializeField(ref _PlayerForce, info, "PlayerForce", true);
        PassThrough.PlayerForce = _PlayerForce;
    }

    public override void GetObjectData(SerializationInfo info, StreamingContext c)
    {
        base.GetObjectData(info, c);
        info.AddValue("PlayerForce", _PlayerForce);
    }

}

 

 

The Custom Editor Scene Object now just needs to be added to the scene like a normal object would.

Putting-It-All-Together

A short obligatory video of how quickly these components can be added to a new project and have a player model moving around interacting with the objects.

 

Hope that this encourages you to build some components and share them with the SunBurn community.

Leave a Reply

Your email address will not be published. Required fields are marked *