// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; using System.Linq; using Xenko.Core; using Xenko.Core.Mathematics; using Xenko.Animations; using Xenko.Engine; using Xenko.Input; using Xenko.Physics; using Xenko.Rendering.Sprites; namespace PhysicsSample { /// /// This script will interface the Physics character controller of the entity to move the character around /// Will also animate the sprite of the entity, between run and idle. /// public class CharacterScript : SyncScript { [Flags] enum PlayerState { Idle = 0x0, Run = 0x1, Jump = 0x2 } private const float speed = 0.075f; private CharacterComponent playerController; private SpriteComponent playerSprite; [DataMember(Mask = LiveScriptingMask)] // keep the value when reloading the script (live-scripting) private bool movingToTarget; [DataMember(Mask = LiveScriptingMask)] // keep the value when reloading the script (live-scripting) private PlayerState oldState = PlayerState.Idle; [DataMember(Mask = LiveScriptingMask)] // keep the value when reloading the script (live-scripting) private Vector3 oldDirection = Vector3.Zero; [DataMember(Mask = LiveScriptingMask)] // keep the value when reloading the script (live-scripting) private Vector3 autoPilotTarget = Vector3.Zero; void PlayIdle() { var sheet = ((SpriteFromSheet)playerSprite.SpriteProvider).Sheet; SpriteAnimation.Play(playerSprite, sheet.FindImageIndex("idle0"), sheet.FindImageIndex("idle4"), AnimationRepeatMode.LoopInfinite, 7); } void PlayRun() { var sheet = ((SpriteFromSheet)playerSprite.SpriteProvider).Sheet; SpriteAnimation.Play(playerSprite, sheet.FindImageIndex("run0"), sheet.FindImageIndex("run4"), AnimationRepeatMode.LoopInfinite, 12); } public override void Start() { playerSprite = Entity.Get(); playerController = Entity.Get(); //Please remember that in the GameStudio element the parameter Step Height is extremely important, it not set properly it will cause the entity to snap fast to the ground playerController.JumpSpeed = 5.0f; playerController.Gravity = new Vector3(0.0f, -10.0f, 0.0f); playerController.FallSpeed = 10.0f; playerController.ProcessCollisions = true; if (!IsLiveReloading) { Script.AddTask(async () => { while (Game.IsRunning) { var collision = await playerController.NewCollision(); // Stop if we collide from side foreach (var contact in collision.Contacts) { if (contact.Normal.X < -0.5f || contact.Normal.X > 0.5f) { movingToTarget = false; break; } } } }); PlayIdle(); } } public override void Update() { var playerState = PlayerState.Idle; var playerDirection = Vector3.Zero; // -- Keyboard Inputs // Space bar = jump if (Input.IsKeyDown(Keys.Space)) { playerState |= PlayerState.Jump; } // Left - right = run if (Input.IsKeyDown(Keys.Right)) { movingToTarget = false; playerState |= PlayerState.Run; playerDirection = Vector3.UnitX * speed; } else if (Input.IsKeyDown(Keys.Left)) { movingToTarget = false; playerState |= PlayerState.Run; playerDirection = -Vector3.UnitX * speed; } // -- Pointer (mouse/touch) foreach (var pointerEvent in Input.PointerEvents.Where(pointerEvent => pointerEvent.EventType == PointerEventType.Pressed)) { if (!movingToTarget) { var screenX = (pointerEvent.Position.X - 0.5f) * 2.0f; screenX *= 8.75f; autoPilotTarget = new Vector3(screenX, 0, 0); movingToTarget = true; } else { playerState |= PlayerState.Jump; } } // -- Logic // are we autopiloting? if (movingToTarget) { var direction = autoPilotTarget - Entity.Transform.Position; direction.Y = 0; //should we stop? var length = direction.Length(); if (length < speed) { movingToTarget = false; playerDirection = Vector3.Zero; playerState = PlayerState.Idle; } else { direction.Normalize(); playerDirection = (direction.X > 0 ? Vector3.UnitX : -Vector3.UnitX) * speed; playerState |= PlayerState.Run; } } // did we start jumping? if (playerState.HasFlag(PlayerState.Jump) && !oldState.HasFlag(PlayerState.Jump)) { playerController.Jump(); } // did we just land? if (oldState.HasFlag(PlayerState.Jump)) { if (!playerController.IsGrounded) { //force set jump flag if (!playerState.HasFlag(PlayerState.Jump)) { playerState |= PlayerState.Jump; // Mantain motion playerDirection = oldDirection; } } else if (playerController.IsGrounded) { //force clear jump flag if (playerState.HasFlag(PlayerState.Jump)) { playerState ^= PlayerState.Jump; } } } // did we start running? if (playerState.HasFlag(PlayerState.Run) && !oldState.HasFlag(PlayerState.Run)) { PlayRun(); } // did we stop running? else if (!playerState.HasFlag(PlayerState.Run) && oldState.HasFlag(PlayerState.Run)) { PlayIdle(); } // movement logic if (oldDirection != playerDirection) { playerController.Move(playerDirection); if (playerState.HasFlag(PlayerState.Run)) { if ((playerDirection.X > 0 && Entity.Transform.Scale.X < 0) || (playerDirection.X < 0 && Entity.Transform.Scale.X > 0)) { Entity.Transform.Scale.X *= -1.0f; } } } // Store current state for next frame oldState = playerState; oldDirection = playerDirection; } } }