您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
339 行
14 KiB
339 行
14 KiB
using System.Collections.Generic;
|
|
using Unity.Entities;
|
|
using Unity.Mathematics;
|
|
using UnityEngine;
|
|
using UnityEngine.Profiling;
|
|
|
|
[RequireComponent(typeof(ReplicatedAbility))]
|
|
public class Ability_Movement : MonoBehaviour
|
|
{
|
|
public struct Settings : IComponentData
|
|
{
|
|
public float UNUSED_moveSpeed;
|
|
}
|
|
|
|
public Settings settings;
|
|
|
|
private void OnEnable()
|
|
{
|
|
var gameObjectEntity = GetComponent<GameObjectEntity>();
|
|
var entityManager = gameObjectEntity.EntityManager;
|
|
var abilityEntity = gameObjectEntity.Entity;
|
|
|
|
// Default components
|
|
entityManager.AddComponentData(abilityEntity, new CharacterAbility());
|
|
entityManager.AddComponentData(abilityEntity, new AbilityControl());
|
|
|
|
// Ability components
|
|
entityManager.AddComponentData(abilityEntity, settings);
|
|
|
|
// Setup replicated ability
|
|
var replicatedAbility = entityManager.GetComponentObject<ReplicatedAbility>(abilityEntity);
|
|
replicatedAbility.predictedHandlers = new IPredictedDataHandler[1];
|
|
replicatedAbility.predictedHandlers[0] = new PredictedEntityHandler<AbilityControl>(entityManager, abilityEntity);
|
|
}
|
|
}
|
|
|
|
|
|
[DisableAutoCreation]
|
|
class Movement_Update : BaseComponentDataSystem<CharacterAbility, Ability_Movement.Settings>
|
|
{
|
|
[ConfigVar(Name = "debug.charactermove", Description = "Show graphs of one character's movement along x, y, z", DefaultValue = "0")]
|
|
public static ConfigVar debugCharacterMove;
|
|
|
|
// Debugging graphs to show player movement in 3 axis
|
|
static float[] movehist_x = new float[100];
|
|
static float[] movehist_y = new float[100];
|
|
static float[] movehist_z = new float[100];
|
|
static float lastUsedFrame;
|
|
|
|
readonly int m_platformLayer;
|
|
readonly int m_charCollisionALayer;
|
|
readonly int m_charCollisionBLayer;
|
|
|
|
public Movement_Update(GameWorld world) : base(world)
|
|
{
|
|
m_platformLayer = LayerMask.NameToLayer("Platform");
|
|
m_charCollisionALayer = LayerMask.NameToLayer("CharCollisionA");
|
|
m_charCollisionBLayer = LayerMask.NameToLayer("CharCollisionB");
|
|
ExtraComponentRequirements = new ComponentType[] { typeof(ServerEntity) } ;
|
|
}
|
|
|
|
protected override void Update(Entity abilityEntity, CharacterAbility charAbility, Ability_Movement.Settings settings )
|
|
{
|
|
Profiler.BeginSample("Movement_Update");
|
|
|
|
var time = m_world.worldTime;
|
|
|
|
var command = EntityManager.GetComponentObject<UserCommandComponent>(charAbility.character).command;
|
|
var characterPredictedState = EntityManager.GetComponentObject<CharacterPredictedState>(charAbility.character);
|
|
var health = EntityManager.GetComponentObject<HealthState>(charAbility.character);
|
|
|
|
var newPhase = CharacterPredictedState.StateData.LocoState.MaxValue;
|
|
|
|
var phaseDuration = time.DurationSinceTick(characterPredictedState.State.locoStartTick);
|
|
|
|
var isOnGround = characterPredictedState.State.IsOnGround();
|
|
var isMoveWanted = command.moveMagnitude != 0.0f;
|
|
|
|
if (health.health <= 0)
|
|
{
|
|
newPhase = CharacterPredictedState.StateData.LocoState.Dead;
|
|
}
|
|
else
|
|
{
|
|
// Ground movement
|
|
if (isOnGround)
|
|
{
|
|
if (isMoveWanted)
|
|
{
|
|
newPhase = CharacterPredictedState.StateData.LocoState.GroundMove;
|
|
}
|
|
else
|
|
{
|
|
newPhase = CharacterPredictedState.StateData.LocoState.Stand;
|
|
}
|
|
}
|
|
|
|
// Jump
|
|
if (isOnGround)
|
|
characterPredictedState.State.jumpCount = 0;
|
|
|
|
if (command.jump && isOnGround)
|
|
{
|
|
characterPredictedState.State.jumpCount = 1;
|
|
newPhase = CharacterPredictedState.StateData.LocoState.Jump;
|
|
}
|
|
|
|
if (command.jump && characterPredictedState.State.locoState == CharacterPredictedState.StateData.LocoState.InAir && characterPredictedState.State.jumpCount < 2)
|
|
{
|
|
characterPredictedState.State.jumpCount = characterPredictedState.State.jumpCount + 1;
|
|
characterPredictedState.State.velocity.y = 0;
|
|
newPhase = CharacterPredictedState.StateData.LocoState.DoubleJump;
|
|
}
|
|
|
|
if (characterPredictedState.State.locoState == CharacterPredictedState.StateData.LocoState.Jump || characterPredictedState.State.locoState == CharacterPredictedState.StateData.LocoState.DoubleJump)
|
|
{
|
|
if (phaseDuration >= Game.config.jumpAscentDuration)
|
|
{
|
|
newPhase = CharacterPredictedState.StateData.LocoState.InAir;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set phase start tick if phase has changed
|
|
if (newPhase != CharacterPredictedState.StateData.LocoState.MaxValue && newPhase != characterPredictedState.State.locoState)
|
|
{
|
|
characterPredictedState.State.locoState = newPhase;
|
|
characterPredictedState.State.locoStartTick = time.tick;
|
|
}
|
|
|
|
if (debugCharacterMove.IntValue > 0)
|
|
{
|
|
// Only show for one player
|
|
if (lastUsedFrame < Time.frameCount)
|
|
{
|
|
lastUsedFrame = Time.frameCount;
|
|
|
|
int o = Time.frameCount % movehist_x.Length;
|
|
movehist_x[o] = characterPredictedState.State.position.x % 10.0f;
|
|
movehist_y[o] = characterPredictedState.State.position.y % 10.0f;
|
|
movehist_z[o] = characterPredictedState.State.position.z % 10.0f;
|
|
|
|
DebugOverlay.DrawGraph(4, 4, 10, 5, movehist_x, o, Color.red, 10.0f);
|
|
DebugOverlay.DrawGraph(4, 12, 10, 5, movehist_y, o, Color.green, 10.0f);
|
|
DebugOverlay.DrawGraph(4, 20, 10, 5, movehist_z, o, Color.blue, 10.0f);
|
|
}
|
|
}
|
|
|
|
if (time.tick != characterPredictedState.State.tick + 1)
|
|
GameDebug.LogError("Update tick invalid. Game tick:" + time.tick + " but current state is at tick:" + characterPredictedState.State.tick);
|
|
|
|
characterPredictedState.State.tick = time.tick;
|
|
|
|
// Apply damange impulse from previus frame
|
|
if (time.tick == characterPredictedState.State.damageTick + 1)
|
|
{
|
|
characterPredictedState.State.velocity += characterPredictedState.State.damageDirection*characterPredictedState.State.damageImpulse;
|
|
characterPredictedState.State.locoState = CharacterPredictedState.StateData.LocoState.InAir;
|
|
characterPredictedState.State.locoStartTick = time.tick;
|
|
}
|
|
|
|
var moveQuery = EntityManager.GetComponentObject<CharacterMoveQuery>(charAbility.character);
|
|
|
|
// Simple adjust of height while on platform
|
|
if (characterPredictedState.State.locoState == CharacterPredictedState.StateData.LocoState.Stand &&
|
|
characterPredictedState.groundCollider != null &&
|
|
characterPredictedState.groundCollider.gameObject.layer == m_platformLayer)
|
|
{
|
|
if (characterPredictedState.altitude < moveQuery.settings.skinWidth - 0.01f )
|
|
{
|
|
var platform = characterPredictedState.groundCollider;
|
|
var posY = platform.transform.position.y + moveQuery.settings.skinWidth;
|
|
characterPredictedState.State.position.y = posY;
|
|
}
|
|
}
|
|
|
|
// Calculate movement and move character
|
|
var deltaPos = Vector3.zero;
|
|
CalculateMovement(ref time, characterPredictedState, ref command, ref deltaPos);
|
|
|
|
// Setup movement query
|
|
moveQuery.collisionLayer = characterPredictedState.teamId == 0 ? m_charCollisionALayer : m_charCollisionBLayer;
|
|
moveQuery.moveQueryStart = characterPredictedState.State.position;
|
|
moveQuery.moveQueryEnd = moveQuery.moveQueryStart + (float3)deltaPos;
|
|
|
|
Profiler.EndSample();
|
|
}
|
|
|
|
void CalculateMovement(ref GameTime gameTime, CharacterPredictedState predictedState, ref UserCommand command, ref Vector3 deltaPos)
|
|
{
|
|
var velocity = predictedState.State.velocity;
|
|
switch (predictedState.State.locoState)
|
|
{
|
|
case CharacterPredictedState.StateData.LocoState.Jump:
|
|
case CharacterPredictedState.StateData.LocoState.DoubleJump:
|
|
|
|
// In jump we overwrite velocity y component with linear movement up
|
|
velocity = CalculateGroundVelocity(velocity, ref command, Game.config.playerSpeed, Game.config.playerAirFriction, Game.config.playerAiracceleration, gameTime.tickDuration);
|
|
velocity.y = Game.config.jumpAscentHeight / Game.config.jumpAscentDuration;
|
|
deltaPos += velocity * gameTime.tickDuration;
|
|
|
|
return;
|
|
case CharacterPredictedState.StateData.LocoState.InAir:
|
|
|
|
var gravity = Game.config.playerGravity;
|
|
velocity += Vector3.down * gravity * gameTime.tickDuration;
|
|
velocity = CalculateGroundVelocity(velocity, ref command, Game.config.playerSpeed, Game.config.playerAirFriction, Game.config.playerAiracceleration, gameTime.tickDuration);
|
|
|
|
if (velocity.y < -Game.config.maxFallVelocity)
|
|
velocity.y = -Game.config.maxFallVelocity;
|
|
|
|
// Cheat movement
|
|
if (command.boost && (Game.GetGameLoop<PreviewGameLoop>() != null))
|
|
{
|
|
velocity.y += 25.0f * gameTime.tickDuration;
|
|
velocity.y = Mathf.Clamp(velocity.y, -2.0f, 10.0f);
|
|
}
|
|
|
|
deltaPos = velocity * gameTime.tickDuration;
|
|
|
|
return;
|
|
case CharacterPredictedState.StateData.LocoState.Dead:
|
|
deltaPos = Vector3.zero;
|
|
return;
|
|
}
|
|
|
|
var playerSpeed = predictedState.State.sprinting ? Game.config.playerSprintSpeed : Game.config.playerSpeed;
|
|
|
|
velocity = CalculateGroundVelocity(velocity, ref command, playerSpeed, Game.config.playerFriction, Game.config.playerAcceleration, gameTime.tickDuration);
|
|
// Debug.DrawLine(predictedState.State.position, predictedState.State.position + velocity, Color.yellow,1 );
|
|
|
|
// Simple follow ground code so character sticks to ground when running down hill
|
|
velocity.y = -400.0f*gameTime.tickDuration;
|
|
|
|
// Debug.DrawLine(predictedState.State.position, predictedState.State.position + velocity, Color.green, 1 );
|
|
|
|
deltaPos = velocity * gameTime.tickDuration;
|
|
}
|
|
|
|
Vector3 CalculateGroundVelocity(Vector3 velocity, ref UserCommand command, float playerSpeed, float friction, float acceleration, float deltaTime)
|
|
{
|
|
var moveYawRotation = Quaternion.Euler(0, command.lookYaw + command.moveYaw, 0);
|
|
var moveVec = moveYawRotation * Vector3.forward * command.moveMagnitude;
|
|
|
|
// Applying friction
|
|
var groundVelocity = new Vector3(velocity.x, 0, velocity.z);
|
|
var groundSpeed = groundVelocity.magnitude;
|
|
var frictionSpeed = Mathf.Max(groundSpeed, 1.0f) * deltaTime * friction;
|
|
var newGroundSpeed = groundSpeed - frictionSpeed;
|
|
if (newGroundSpeed < 0)
|
|
newGroundSpeed = 0;
|
|
if (groundSpeed > 0)
|
|
groundVelocity *= (newGroundSpeed / groundSpeed);
|
|
|
|
// Doing actual movement (q2 style)
|
|
var wantedGroundVelocity = moveVec * playerSpeed;
|
|
var wantedGroundDir = wantedGroundVelocity.normalized;
|
|
var currentSpeed = Vector3.Dot(wantedGroundDir, groundVelocity);
|
|
var wantedSpeed = playerSpeed;
|
|
var deltaSpeed = wantedSpeed - currentSpeed;
|
|
if (deltaSpeed > 0.0f)
|
|
{
|
|
var accel = deltaTime * acceleration * playerSpeed;
|
|
var speed_adjustment = Mathf.Clamp(accel, 0.0f, deltaSpeed) * wantedGroundDir;
|
|
groundVelocity += speed_adjustment;
|
|
}
|
|
|
|
if (!Game.config.easterBunny)
|
|
{
|
|
newGroundSpeed = groundVelocity.magnitude;
|
|
if (newGroundSpeed > playerSpeed)
|
|
groundVelocity *= playerSpeed / newGroundSpeed;
|
|
}
|
|
|
|
velocity.x = groundVelocity.x;
|
|
velocity.z = groundVelocity.z;
|
|
|
|
return velocity;
|
|
}
|
|
}
|
|
|
|
[DisableAutoCreation]
|
|
class Movement_HandleCollision : BaseComponentDataSystem<CharacterAbility>
|
|
{
|
|
public Movement_HandleCollision(GameWorld world) : base(world)
|
|
{
|
|
ExtraComponentRequirements = new ComponentType[] { typeof(ServerEntity) } ;
|
|
}
|
|
|
|
protected override void Update(Entity abilityEntity, CharacterAbility charAbility)
|
|
{
|
|
Profiler.BeginSample("Movement_HandleCollision");
|
|
|
|
|
|
|
|
|
|
|
|
var time = m_world.worldTime;
|
|
var character = EntityManager.GetComponentObject<CharacterPredictedState>(charAbility.character);
|
|
var query = EntityManager.GetComponentObject<CharacterMoveQuery>(charAbility.character);
|
|
var command = EntityManager.GetComponentObject<UserCommandComponent>(charAbility.character).command;
|
|
|
|
// Check for ground change (hitting ground or leaving ground)
|
|
if (character.State.locoState != CharacterPredictedState.StateData.LocoState.Dead)
|
|
{
|
|
var isOnGround = character.State.IsOnGround();
|
|
if (isOnGround != query.isGrounded)
|
|
{
|
|
if (query.isGrounded)
|
|
{
|
|
if (command.moveMagnitude != 0.0f)
|
|
{
|
|
character.State.locoState = CharacterPredictedState.StateData.LocoState.GroundMove;
|
|
}
|
|
else
|
|
{
|
|
character.State.locoState = CharacterPredictedState.StateData.LocoState.Stand;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
character.State.locoState = CharacterPredictedState.StateData.LocoState.InAir;
|
|
}
|
|
|
|
character.State.locoStartTick = time.tick;
|
|
}
|
|
}
|
|
|
|
// Manually calculate resulting velocity as characterController.velocity is linked to Time.deltaTime
|
|
var newPos = query.moveQueryResult;
|
|
var oldPos = query.moveQueryStart;
|
|
var velocity = (newPos - oldPos) / time.tickDuration;
|
|
|
|
character.State.velocity = velocity;
|
|
character.State.position = query.moveQueryResult;
|
|
|
|
Profiler.EndSample();
|
|
}
|
|
}
|