Thursday, February 21, 2013

Developers Log. Development Date 95. Controls!

The main focus of this week was controls. I started off the week working on adding the ability to slightly adjust velocity for left/right movement. Last week I locked movement when in air, but I wanted the player to be able to add small corrections to their landing position. I accomplished this by adding two methods to the inputController (well, I slightly modified one we had been using, then added the other.)

 public override Vector3 GetWorldVectorRelativeToCamera()  
 {  
    float horizontalValue = ThumbSticks.Left.X;  
    float verticalValue = ThumbSticks.Left.Y;  
   
    Matrix cam = Matrix.Invert(Camera.View);  
   
    Vector3 vec = (cam.Forward * verticalValue) + (cam.Right * horizontalValue);  
    return new Vector3(vec.X, 0, vec.Z);  
 }  
   
 public override Vector2 GetLeftThumbstickRelativeToCamera()  
 {  
    Vector3 directionVec = GetWorldVectorRelativeToCamera();  
    float horizontalValue = Vector3.Dot(directionVec, physicsBody.Orientation.Right);  
    float verticalValue = Vector3.Dot(directionVec, physicsBody.Orientation.Forward);  
            
    return new Vector2(horizontalValue, verticalValue);  
 }  

GetWorldVectorRelativeToCamera returns the vector in world space of the left thumbstick. So if the player press forward, it returns the vector of forward from the camera (the camera can rotate 360 degrees around the player.)

GetLeftThumbstickRelativeToCamera returns the value one expects from calling Thumbsticks.Left but relative to the camera and player. This came into play for when the player was locked in a certain orientation. The player could be jumping in a direction that is equivalent to the camera's right. If the player pressed down on the thumbstick, the controls need to be the same as when the player is on the ground. If the player is on the ground and presses down, he runs at the camera. If the player is jumping right and presses down, he needs to slowly move towards the camera, still keeping his velocity and orientation going to the right of the camera.

In addition to in air controls, I added a new sliding state, for when the player slides down a wall, changed horizontal wall runs to cause the player to fall after 500 milliseconds rather than auto jumping for them. Requiring the player to jump rather than doing it for him/her adds a better feeling of play. Also, I now account for thumbstick directions when jumping to slightly alter the jump direction. This value needs testing, but it might be too small of an influence currently.

Outside of controls I added a few features to our level editor to make creating triggers easier on the level designer. I finished connecting all of the trigger creation for killzones, checkpoints, and ability pickup. Earlier in the week I helped architect the way we wanted to do triggers, I wanted to ensure we were taking advantage of our component/entity system.

We have a trigger component that listens for collisions from the physics body. This trigger can also have a flag set for what can trigger it (just a player, player and AI, bullets, etc.) When triggered, it raises events so that any other component that subscribed to the trigger will be notified. This is sort of a shadow to Unity3d's broadcast functionality, but I prefer this method.

Here is an example of how this pieces together:

Trigger class
 public class Trigger : Component  
 {    
     public delegate void TriggerEnter(Entity entity);  
     /// <summary>  
    /// Methods to be invoked upon the appropriate entity colliding with this trigger.   
    /// </summary>  
     public event TriggerEnter OnTriggerEnter;   
   
     ... [within collision handling, relative to whatever physics engine you are using]  
     if (OnTriggerEnter != null)   
       OnTriggerEnter.Invoke(entity);  
     ...  
  }  

Some component that uses the trigger.
 public class ExampleComponent: Component  
  {     
     public override void Initialize()  
     {  
       Trigger trigger = OwnerEntity.GetComponent<Trigger>();  
       if (trigger == null) 
           throw new System.NullReferenceException("Trigger component did not exist on owner entity");  
       trigger.OnTriggerEnter += OnCollide;  
     }  
   
     protected override void OnCollide(Entity entity)  
     {  
         // Do something, play a sound, do damage, etc  
       OwnerEntity.Dispose(); // delete trigger  
     }  
 }  

Using this method, I can attach several components to an entity that has a Trigger component, and each one can listen for the on collide, and activate their respective code when that happens. Very dynamic, flexible, light, and powerful.

No comments:

Post a Comment