2018-06-19 09:06:54 +00:00
|
|
|
// 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 Xenko.Core;
|
|
|
|
|
using Xenko.Core.Mathematics;
|
|
|
|
|
using Xenko.Animations;
|
|
|
|
|
using Xenko.Engine;
|
|
|
|
|
using Xenko.Graphics;
|
|
|
|
|
using Xenko.Input;
|
|
|
|
|
using Xenko.Physics;
|
|
|
|
|
using Xenko.Rendering.Sprites;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace SpriteStudioDemo
|
|
|
|
|
{
|
|
|
|
|
public class PlayerScript : AsyncScript
|
|
|
|
|
{
|
|
|
|
|
private enum AgentAnimation
|
|
|
|
|
{
|
|
|
|
|
Run,
|
|
|
|
|
Idle,
|
|
|
|
|
Shoot
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// InputState represents all command inputs from a user
|
|
|
|
|
private enum InputState
|
|
|
|
|
{
|
|
|
|
|
None,
|
|
|
|
|
RunLeft,
|
|
|
|
|
RunRight,
|
|
|
|
|
Shoot,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO centralize
|
|
|
|
|
private const float gameWidthX = 16f; // from -8f to 8f
|
|
|
|
|
|
|
|
|
|
private const float gameWidthHalfX = gameWidthX / 2f;
|
|
|
|
|
|
|
|
|
|
private const int AgentMoveDistance = 10; // virtual resolution unit/second
|
|
|
|
|
private const float AgentShootDelay = 0.3f; // second
|
|
|
|
|
|
|
|
|
|
private static readonly Dictionary<AgentAnimation, int> AnimationFps = new Dictionary<AgentAnimation, int> { { AgentAnimation.Run, 12 }, { AgentAnimation.Idle, 7 }, { AgentAnimation.Shoot, 15 } };
|
|
|
|
|
|
|
|
|
|
private SpriteComponent agentSpriteComponent;
|
|
|
|
|
private SpriteSheet spriteSheet;
|
|
|
|
|
|
|
|
|
|
// Touch input state
|
|
|
|
|
private PointerEvent pointerState;
|
|
|
|
|
|
|
|
|
|
private bool isPointerDown; // Cache state if a user is current touching the screen.
|
|
|
|
|
|
|
|
|
|
[DataMember(Mask = LiveScriptingMask)] // keep the value when reloading the script (live-scripting)
|
|
|
|
|
private bool isAgentFacingRight;
|
|
|
|
|
|
|
|
|
|
[DataMember(Mask = LiveScriptingMask)] // keep the value when reloading the script (live-scripting)
|
|
|
|
|
private float shootDelayCounter;
|
|
|
|
|
|
|
|
|
|
[DataMember(Mask = LiveScriptingMask)] // keep the value when reloading the script (live-scripting)
|
|
|
|
|
private AgentAnimation currentAgentAnimation;
|
|
|
|
|
|
|
|
|
|
private AgentAnimation CurrentAgentAnimation { get; set; }
|
|
|
|
|
|
|
|
|
|
public SpriteSheet BulletSheet { get; set; }
|
|
|
|
|
|
|
|
|
|
public PhysicsColliderShape BulletColliderShape { get; set; }
|
|
|
|
|
|
|
|
|
|
public override async Task Execute()
|
|
|
|
|
{
|
|
|
|
|
spriteSheet = BulletSheet;
|
|
|
|
|
agentSpriteComponent = Entity.Get<SpriteComponent>();
|
|
|
|
|
var animComponent = Entity.Get<AnimationComponent>();
|
|
|
|
|
PlayingAnimation playingAnimation = null;
|
|
|
|
|
|
|
|
|
|
// Calculate offset of the bullet from the Agent if he is facing left and right side // TODO improve this
|
|
|
|
|
var bulletOffset = new Vector3(1.3f, 1.65f, 0f);
|
|
|
|
|
|
|
|
|
|
// Initialize game entities
|
|
|
|
|
if (!IsLiveReloading)
|
|
|
|
|
{
|
|
|
|
|
shootDelayCounter = 0f;
|
|
|
|
|
isAgentFacingRight = true;
|
|
|
|
|
currentAgentAnimation = AgentAnimation.Idle;
|
|
|
|
|
}
|
|
|
|
|
CurrentAgentAnimation = currentAgentAnimation;
|
|
|
|
|
|
|
|
|
|
var normalScaleX = Entity.Transform.Scale.X;
|
|
|
|
|
|
|
|
|
|
var bulletCS = BulletColliderShape;
|
|
|
|
|
|
|
|
|
|
Task animTask = null;
|
|
|
|
|
|
|
|
|
|
while (Game.IsRunning)
|
|
|
|
|
{
|
|
|
|
|
await Script.NextFrame();
|
|
|
|
|
|
|
|
|
|
var inputState = GetKeyboardInputState();
|
|
|
|
|
|
|
|
|
|
if (inputState == InputState.None)
|
|
|
|
|
inputState = GetPointerInputState();
|
|
|
|
|
|
|
|
|
|
if (inputState == InputState.RunLeft || inputState == InputState.RunRight)
|
|
|
|
|
{
|
|
|
|
|
// Update Agent's position
|
|
|
|
|
var dt = (float)Game.UpdateTime.Elapsed.TotalSeconds;
|
|
|
|
|
|
|
|
|
|
Entity.Transform.Position.X += ((inputState == InputState.RunRight) ? AgentMoveDistance : -AgentMoveDistance) * dt;
|
|
|
|
|
|
|
|
|
|
if (Entity.Transform.Position.X < -gameWidthHalfX)
|
|
|
|
|
Entity.Transform.Position.X = -gameWidthHalfX;
|
|
|
|
|
|
|
|
|
|
if (Entity.Transform.Position.X > gameWidthHalfX)
|
|
|
|
|
Entity.Transform.Position.X = gameWidthHalfX;
|
|
|
|
|
|
|
|
|
|
isAgentFacingRight = inputState == InputState.RunRight;
|
|
|
|
|
|
|
|
|
|
// If agent face left, flip the sprite
|
|
|
|
|
Entity.Transform.Scale.X = isAgentFacingRight ? normalScaleX : -normalScaleX;
|
|
|
|
|
|
|
|
|
|
// Update the sprite animation and state
|
|
|
|
|
CurrentAgentAnimation = AgentAnimation.Run;
|
|
|
|
|
if (playingAnimation == null || playingAnimation.Name != "Run")
|
|
|
|
|
{
|
|
|
|
|
playingAnimation = animComponent.Play("Run");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (inputState == InputState.Shoot)
|
|
|
|
|
{
|
|
|
|
|
if(animTask != null && !animTask.IsCompleted) continue;
|
|
|
|
|
if (animTask != null && animTask.IsCompleted) playingAnimation = null;
|
|
|
|
|
|
|
|
|
|
animTask = null;
|
|
|
|
|
|
|
|
|
|
var rb = new RigidbodyComponent { CanCollideWith = CollisionFilterGroupFlags.CustomFilter1, CollisionGroup = CollisionFilterGroups.DefaultFilter };
|
|
|
|
|
rb.ColliderShapes.Add(new ColliderShapeAssetDesc { Shape = bulletCS });
|
|
|
|
|
|
|
|
|
|
// Spawns a new bullet
|
|
|
|
|
var bullet = new Entity
|
|
|
|
|
{
|
|
|
|
|
new SpriteComponent { SpriteProvider = SpriteFromSheet.Create(spriteSheet, "bullet") },
|
|
|
|
|
rb,
|
|
|
|
|
new BeamScript()
|
|
|
|
|
};
|
|
|
|
|
bullet.Name = "bullet";
|
|
|
|
|
|
|
|
|
|
bullet.Transform.Position = (isAgentFacingRight) ? Entity.Transform.Position + bulletOffset : Entity.Transform.Position + (bulletOffset * new Vector3(-1, 1, 1));
|
|
|
|
|
bullet.Transform.UpdateWorldMatrix();
|
|
|
|
|
|
|
|
|
|
SceneSystem.SceneInstance.RootScene.Entities.Add(bullet);
|
|
|
|
|
|
|
|
|
|
rb.LinearFactor = new Vector3(1, 0, 0);
|
|
|
|
|
rb.AngularFactor = new Vector3(0, 0, 0);
|
|
|
|
|
rb.ApplyImpulse(isAgentFacingRight ? new Vector3(25, 0, 0) : new Vector3(-25, 0, 0));
|
|
|
|
|
|
|
|
|
|
// Start animation for shooting
|
|
|
|
|
CurrentAgentAnimation = AgentAnimation.Shoot;
|
|
|
|
|
if (playingAnimation == null || playingAnimation.Name != "Attack")
|
|
|
|
|
{
|
|
|
|
|
playingAnimation = animComponent.Play("Attack");
|
|
|
|
|
animTask = animComponent.Ended(playingAnimation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
CurrentAgentAnimation = AgentAnimation.Idle;
|
|
|
|
|
if (playingAnimation == null || playingAnimation.Name != "Stance")
|
|
|
|
|
{
|
|
|
|
|
playingAnimation = animComponent.Play("Stance");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Determine input from a user from a keyboard.
|
|
|
|
|
/// Left and Right arrow for running to left and right direction, Space for shooting.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private InputState GetKeyboardInputState()
|
|
|
|
|
{
|
|
|
|
|
if (Input.IsKeyDown(Keys.Right))
|
|
|
|
|
return InputState.RunRight;
|
|
|
|
|
if (Input.IsKeyDown(Keys.Left))
|
|
|
|
|
return InputState.RunLeft;
|
|
|
|
|
|
|
|
|
|
return Input.IsKeyDown(Keys.Space) ? InputState.Shoot : InputState.None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Determine input from a user from Pointer (Touch/Mouse).
|
|
|
|
|
/// It analyses the input from a user, and transform it to InputState using in the game, which is then returned.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private InputState GetPointerInputState()
|
|
|
|
|
{
|
|
|
|
|
// Get new state of Pointer (Touch input)
|
|
|
|
|
if (Input.PointerEvents.Any())
|
|
|
|
|
{
|
|
|
|
|
var lastPointer = Input.PointerEvents.Last();
|
2018-08-01 09:09:07 +00:00
|
|
|
if (lastPointer.EventType == PointerEventType.Pressed)
|
|
|
|
|
isPointerDown = true;
|
|
|
|
|
else if (lastPointer.EventType == PointerEventType.Released)
|
|
|
|
|
isPointerDown = false;
|
2018-06-19 09:06:54 +00:00
|
|
|
pointerState = lastPointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If a user does not touch the screen, there is not input
|
|
|
|
|
if (!isPointerDown)
|
|
|
|
|
{
|
|
|
|
|
return InputState.None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Transform pointer's position from normorlize coordinate to virtual resolution coordinate
|
|
|
|
|
var resolution = new Vector2(GraphicsDevice.Presenter.BackBuffer.Width, GraphicsDevice.Presenter.BackBuffer.Height);
|
|
|
|
|
var virtualCoordinatePointerPositionA = resolution.X * (pointerState.Position.X + 0.05f);
|
|
|
|
|
var virtualCoordinatePointerPositionB = resolution.X * (pointerState.Position.X - 0.05f);
|
|
|
|
|
|
|
|
|
|
var virtualX = VirtualCoordToPixel(Entity.Transform.Position.X);
|
|
|
|
|
|
|
|
|
|
// Check if the touch position is in the x-axis region of the agent's sprite; if so, input is shoot
|
|
|
|
|
if (virtualX <= virtualCoordinatePointerPositionA && virtualCoordinatePointerPositionB <= virtualX)
|
|
|
|
|
{
|
|
|
|
|
return InputState.Shoot;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if a pointer falls left or right of the screen, which would correspond to Run to the left or right respectively
|
|
|
|
|
return ((pointerState.Position.X) <= virtualX / resolution.X) ? InputState.RunLeft : InputState.RunRight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private float VirtualCoordToPixel(float virtualCoord)
|
|
|
|
|
{
|
|
|
|
|
return (virtualCoord + (gameWidthHalfX)) / gameWidthX * GraphicsDevice.Presenter.BackBuffer.Width;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|