mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 02:50:32 +00:00
feat: Updated example controllers
RuntimeData struct for inspector folding
This commit is contained in:
parent
53d8812281
commit
70351d96f4
@ -22,7 +22,7 @@ public struct OptionsKeys
|
|||||||
public KeyCode ToggleUI;
|
public KeyCode ToggleUI;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum GroundState : byte { Jumping, Falling, Grounded }
|
public enum GroundState : byte { Grounded, Jumping, Falling }
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public struct MoveKeys
|
public struct MoveKeys
|
||||||
@ -145,46 +145,123 @@ public enum ControlOptions : byte
|
|||||||
[Tooltip("Roll acceleration in degrees per second squared")]
|
[Tooltip("Roll acceleration in degrees per second squared")]
|
||||||
public float rollAcceleration = 3f;
|
public float rollAcceleration = 3f;
|
||||||
|
|
||||||
|
// Runtime data in a struct so it can be folded up in inspector
|
||||||
|
[Serializable]
|
||||||
|
public struct RuntimeData
|
||||||
|
{
|
||||||
|
[ReadOnly, SerializeField, Range(-1f, 1f)] float _horizontal;
|
||||||
|
[ReadOnly, SerializeField, Range(-1f, 1f)] float _vertical;
|
||||||
|
[ReadOnly, SerializeField, Range(-300f, 300f)] float _turnSpeed;
|
||||||
|
[ReadOnly, SerializeField, Range(-180f, 180f)] float _pitchAngle;
|
||||||
|
[ReadOnly, SerializeField, Range(-180f, 180f)] float _pitchSpeed;
|
||||||
|
[ReadOnly, SerializeField, Range(-180f, 180f)] float _rollAngle;
|
||||||
|
[ReadOnly, SerializeField, Range(-180f, 180f)] float _rollSpeed;
|
||||||
|
[ReadOnly, SerializeField, Range(-1.5f, 1.5f)] float _animVelocity;
|
||||||
|
[ReadOnly, SerializeField, Range(-1.5f, 1.5f)] float _animRotation;
|
||||||
|
[ReadOnly, SerializeField, Range(-1f, 1f)] float _mouseInputX;
|
||||||
|
[ReadOnly, SerializeField, Range(0, 30f)] float _mouseSensitivity;
|
||||||
|
[ReadOnly, SerializeField] GroundState _groundState;
|
||||||
|
[ReadOnly, SerializeField] Vector3 _direction;
|
||||||
|
[ReadOnly, SerializeField] Vector3Int _velocity;
|
||||||
|
[ReadOnly, SerializeField] GameObject _controllerUI;
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
public float horizontal
|
||||||
|
{
|
||||||
|
get => _horizontal;
|
||||||
|
internal set => _horizontal = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float vertical
|
||||||
|
{
|
||||||
|
get => _vertical;
|
||||||
|
internal set => _vertical = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float turnSpeed
|
||||||
|
{
|
||||||
|
get => _turnSpeed;
|
||||||
|
internal set => _turnSpeed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float pitchAngle
|
||||||
|
{
|
||||||
|
get => _pitchAngle;
|
||||||
|
internal set => _pitchAngle = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float pitchSpeed
|
||||||
|
{
|
||||||
|
get => _pitchSpeed;
|
||||||
|
internal set => _pitchSpeed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float rollAngle
|
||||||
|
{
|
||||||
|
get => _rollAngle;
|
||||||
|
internal set => _rollAngle = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float rollSpeed
|
||||||
|
{
|
||||||
|
get => _rollSpeed;
|
||||||
|
internal set => _rollSpeed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float animVelocity
|
||||||
|
{
|
||||||
|
get => _animVelocity;
|
||||||
|
internal set => _animVelocity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float animRotation
|
||||||
|
{
|
||||||
|
get => _animRotation;
|
||||||
|
internal set => _animRotation = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float mouseInputX
|
||||||
|
{
|
||||||
|
get => _mouseInputX;
|
||||||
|
internal set => _mouseInputX = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float mouseSensitivity
|
||||||
|
{
|
||||||
|
get => _mouseSensitivity;
|
||||||
|
internal set => _mouseSensitivity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroundState groundState
|
||||||
|
{
|
||||||
|
get => _groundState;
|
||||||
|
internal set => _groundState = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 direction
|
||||||
|
{
|
||||||
|
get => _direction;
|
||||||
|
internal set => _direction = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3Int velocity
|
||||||
|
{
|
||||||
|
get => _velocity;
|
||||||
|
internal set => _velocity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameObject controllerUI
|
||||||
|
{
|
||||||
|
get => _controllerUI;
|
||||||
|
internal set => _controllerUI = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
[Header("Diagnostics")]
|
[Header("Diagnostics")]
|
||||||
[ReadOnly, SerializeField]
|
public RuntimeData runtimeData;
|
||||||
GroundState groundState = GroundState.Grounded;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField, Range(-1f, 1f)]
|
|
||||||
float horizontal;
|
|
||||||
[ReadOnly, SerializeField, Range(-1f, 1f)]
|
|
||||||
float vertical;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField, Range(-1f, 1f)]
|
|
||||||
float mouseInputX;
|
|
||||||
[ReadOnly, SerializeField, Range(0, 30f)]
|
|
||||||
float mouseSensitivity;
|
|
||||||
[ReadOnly, SerializeField, Range(-300f, 300f)]
|
|
||||||
float turnSpeed;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField, Range(-180f, 180f)]
|
|
||||||
float pitchAngle;
|
|
||||||
[ReadOnly, SerializeField, Range(-180f, 180f)]
|
|
||||||
float pitchSpeed;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField, Range(-180f, 180f)]
|
|
||||||
float rollAngle;
|
|
||||||
[ReadOnly, SerializeField, Range(-180f, 180f)]
|
|
||||||
float rollSpeed;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField, Range(-1.5f, 1.5f)]
|
|
||||||
float animVelocity;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField, Range(-1.5f, 1.5f)]
|
|
||||||
float animRotation;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField]
|
|
||||||
Vector3 direction;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField]
|
|
||||||
Vector3Int velocity;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField]
|
|
||||||
GameObject controllerUI;
|
|
||||||
|
|
||||||
#region Network Setup
|
#region Network Setup
|
||||||
|
|
||||||
@ -235,8 +312,7 @@ public override void OnStartAuthority()
|
|||||||
{
|
{
|
||||||
// Calculate DPI-aware sensitivity
|
// Calculate DPI-aware sensitivity
|
||||||
float dpiScale = (Screen.dpi > 0) ? (Screen.dpi / BASE_DPI) : 1f;
|
float dpiScale = (Screen.dpi > 0) ? (Screen.dpi / BASE_DPI) : 1f;
|
||||||
mouseSensitivity = turnAcceleration * dpiScale;
|
runtimeData.mouseSensitivity = turnAcceleration * dpiScale;
|
||||||
//Debug.Log($"Screen DPI: {Screen.dpi}, DPI Scale: {dpiScale}, Adjusted Turn Acceleration: {turnAccelerationDPI}");
|
|
||||||
|
|
||||||
SetCursor(controlOptions.HasFlag(ControlOptions.MouseSteer));
|
SetCursor(controlOptions.HasFlag(ControlOptions.MouseSteer));
|
||||||
|
|
||||||
@ -262,22 +338,22 @@ public override void OnStopAuthority()
|
|||||||
public override void OnStartLocalPlayer()
|
public override void OnStartLocalPlayer()
|
||||||
{
|
{
|
||||||
if (ControllerUIPrefab != null)
|
if (ControllerUIPrefab != null)
|
||||||
controllerUI = Instantiate(ControllerUIPrefab);
|
runtimeData.controllerUI = Instantiate(ControllerUIPrefab);
|
||||||
|
|
||||||
if (controllerUI != null)
|
if (runtimeData.controllerUI != null)
|
||||||
{
|
{
|
||||||
if (controllerUI.TryGetComponent(out FlyerControllerUI canvasControlPanel))
|
if (runtimeData.controllerUI.TryGetComponent(out FlyerControllerUI canvasControlPanel))
|
||||||
canvasControlPanel.Refresh(moveKeys, flightKeys, optionsKeys);
|
canvasControlPanel.Refresh(moveKeys, flightKeys, optionsKeys);
|
||||||
|
|
||||||
controllerUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
runtimeData.controllerUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnStopLocalPlayer()
|
public override void OnStopLocalPlayer()
|
||||||
{
|
{
|
||||||
if (controllerUI != null)
|
if (runtimeData.controllerUI != null)
|
||||||
Destroy(controllerUI);
|
Destroy(runtimeData.controllerUI);
|
||||||
controllerUI = null;
|
runtimeData.controllerUI = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -306,12 +382,12 @@ void Update()
|
|||||||
|
|
||||||
// Reset ground state
|
// Reset ground state
|
||||||
if (characterController.isGrounded)
|
if (characterController.isGrounded)
|
||||||
groundState = GroundState.Grounded;
|
runtimeData.groundState = GroundState.Grounded;
|
||||||
else if (groundState != GroundState.Jumping)
|
else if (runtimeData.groundState != GroundState.Jumping)
|
||||||
groundState = GroundState.Falling;
|
runtimeData.groundState = GroundState.Falling;
|
||||||
|
|
||||||
// Diagnostic velocity...FloorToInt for display purposes
|
// Diagnostic velocity...FloorToInt for display purposes
|
||||||
velocity = Vector3Int.FloorToInt(characterController.velocity);
|
runtimeData.velocity = Vector3Int.FloorToInt(characterController.velocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetCursor(bool locked)
|
void SetCursor(bool locked)
|
||||||
@ -335,8 +411,8 @@ void HandleOptions()
|
|||||||
{
|
{
|
||||||
controlOptions ^= ControlOptions.ShowUI;
|
controlOptions ^= ControlOptions.ShowUI;
|
||||||
|
|
||||||
if (controllerUI != null)
|
if (runtimeData.controllerUI != null)
|
||||||
controllerUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
runtimeData.controllerUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flightKeys.AutoLevel != KeyCode.None && Input.GetKeyUp(flightKeys.AutoLevel))
|
if (flightKeys.AutoLevel != KeyCode.None && Input.GetKeyUp(flightKeys.AutoLevel))
|
||||||
@ -354,28 +430,28 @@ void HandleTurning(float deltaTime)
|
|||||||
if (moveKeys.TurnRight != KeyCode.None && Input.GetKey(moveKeys.TurnRight))
|
if (moveKeys.TurnRight != KeyCode.None && Input.GetKey(moveKeys.TurnRight))
|
||||||
targetTurnSpeed += maxTurnSpeed;
|
targetTurnSpeed += maxTurnSpeed;
|
||||||
|
|
||||||
turnSpeed = Mathf.MoveTowards(turnSpeed, targetTurnSpeed, turnAcceleration * maxTurnSpeed * deltaTime);
|
runtimeData.turnSpeed = Mathf.MoveTowards(runtimeData.turnSpeed, targetTurnSpeed, turnAcceleration * maxTurnSpeed * deltaTime);
|
||||||
transform.Rotate(0f, turnSpeed * deltaTime, 0f);
|
transform.Rotate(0f, runtimeData.turnSpeed * deltaTime, 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleMouseSteer(float deltaTime)
|
void HandleMouseSteer(float deltaTime)
|
||||||
{
|
{
|
||||||
// Accumulate mouse input over time
|
// Accumulate mouse input over time
|
||||||
mouseInputX += Input.GetAxisRaw("Mouse X") * mouseSensitivity;
|
runtimeData.mouseInputX += Input.GetAxisRaw("Mouse X") * runtimeData.mouseSensitivity;
|
||||||
|
|
||||||
// Clamp the accumulator to simulate key press behavior
|
// Clamp the accumulator to simulate key press behavior
|
||||||
mouseInputX = Mathf.Clamp(mouseInputX, -1f, 1f);
|
runtimeData.mouseInputX = Mathf.Clamp(runtimeData.mouseInputX, -1f, 1f);
|
||||||
|
|
||||||
// Calculate target turn speed
|
// Calculate target turn speed
|
||||||
float targetTurnSpeed = mouseInputX * maxTurnSpeed;
|
float targetTurnSpeed = runtimeData.mouseInputX * maxTurnSpeed;
|
||||||
|
|
||||||
// Use the same acceleration logic as HandleTurning
|
// Use the same acceleration logic as HandleTurning
|
||||||
turnSpeed = Mathf.MoveTowards(turnSpeed, targetTurnSpeed, mouseSensitivity * maxTurnSpeed * deltaTime);
|
runtimeData.turnSpeed = Mathf.MoveTowards(runtimeData.turnSpeed, targetTurnSpeed, runtimeData.mouseSensitivity * maxTurnSpeed * deltaTime);
|
||||||
|
|
||||||
// Apply rotation
|
// Apply rotation
|
||||||
transform.Rotate(0f, turnSpeed * deltaTime, 0f);
|
transform.Rotate(0f, runtimeData.turnSpeed * deltaTime, 0f);
|
||||||
|
|
||||||
mouseInputX = Mathf.MoveTowards(mouseInputX, 0f, mouseSensitivity * deltaTime);
|
runtimeData.mouseInputX = Mathf.MoveTowards(runtimeData.mouseInputX, 0f, runtimeData.mouseSensitivity * deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandlePitch(float deltaTime)
|
void HandlePitch(float deltaTime)
|
||||||
@ -396,15 +472,15 @@ void HandlePitch(float deltaTime)
|
|||||||
inputDetected = true;
|
inputDetected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pitchSpeed = Mathf.MoveTowards(pitchSpeed, targetPitchSpeed, pitchAcceleration * maxPitchSpeed * deltaTime);
|
runtimeData.pitchSpeed = Mathf.MoveTowards(runtimeData.pitchSpeed, targetPitchSpeed, pitchAcceleration * maxPitchSpeed * deltaTime);
|
||||||
|
|
||||||
// Apply pitch rotation
|
// Apply pitch rotation
|
||||||
pitchAngle += pitchSpeed * deltaTime;
|
runtimeData.pitchAngle += runtimeData.pitchSpeed * deltaTime;
|
||||||
pitchAngle = Mathf.Clamp(pitchAngle, -maxPitchUpAngle, maxPitchDownAngle);
|
runtimeData.pitchAngle = Mathf.Clamp(runtimeData.pitchAngle, -maxPitchUpAngle, maxPitchDownAngle);
|
||||||
|
|
||||||
// Return to zero when no input
|
// Return to zero when no input
|
||||||
if (!inputDetected && controlOptions.HasFlag(ControlOptions.AutoLevel))
|
if (!inputDetected && controlOptions.HasFlag(ControlOptions.AutoLevel))
|
||||||
pitchAngle = Mathf.MoveTowards(pitchAngle, 0f, maxPitchSpeed * deltaTime);
|
runtimeData.pitchAngle = Mathf.MoveTowards(runtimeData.pitchAngle, 0f, maxPitchSpeed * deltaTime);
|
||||||
|
|
||||||
ApplyRotation();
|
ApplyRotation();
|
||||||
}
|
}
|
||||||
@ -427,15 +503,15 @@ void HandleRoll(float deltaTime)
|
|||||||
inputDetected = true;
|
inputDetected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
rollSpeed = Mathf.MoveTowards(rollSpeed, targetRollSpeed, rollAcceleration * maxRollSpeed * deltaTime);
|
runtimeData.rollSpeed = Mathf.MoveTowards(runtimeData.rollSpeed, targetRollSpeed, rollAcceleration * maxRollSpeed * deltaTime);
|
||||||
|
|
||||||
// Apply roll rotation
|
// Apply roll rotation
|
||||||
rollAngle += rollSpeed * deltaTime;
|
runtimeData.rollAngle += runtimeData.rollSpeed * deltaTime;
|
||||||
rollAngle = Mathf.Clamp(rollAngle, -maxRollAngle, maxRollAngle);
|
runtimeData.rollAngle = Mathf.Clamp(runtimeData.rollAngle, -maxRollAngle, maxRollAngle);
|
||||||
|
|
||||||
// Return to zero when no input
|
// Return to zero when no input
|
||||||
if (!inputDetected && controlOptions.HasFlag(ControlOptions.AutoLevel))
|
if (!inputDetected && controlOptions.HasFlag(ControlOptions.AutoLevel))
|
||||||
rollAngle = Mathf.MoveTowards(rollAngle, 0f, maxRollSpeed * deltaTime);
|
runtimeData.rollAngle = Mathf.MoveTowards(runtimeData.rollAngle, 0f, maxRollSpeed * deltaTime);
|
||||||
|
|
||||||
ApplyRotation();
|
ApplyRotation();
|
||||||
}
|
}
|
||||||
@ -446,7 +522,7 @@ void ApplyRotation()
|
|||||||
float currentYaw = transform.localRotation.eulerAngles.y;
|
float currentYaw = transform.localRotation.eulerAngles.y;
|
||||||
|
|
||||||
// Apply all rotations
|
// Apply all rotations
|
||||||
transform.localRotation = Quaternion.Euler(pitchAngle, currentYaw, rollAngle);
|
transform.localRotation = Quaternion.Euler(runtimeData.pitchAngle, currentYaw, runtimeData.rollAngle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleMove(float deltaTime)
|
void HandleMove(float deltaTime)
|
||||||
@ -464,39 +540,36 @@ void HandleMove(float deltaTime)
|
|||||||
if (targetMoveX == 0f)
|
if (targetMoveX == 0f)
|
||||||
{
|
{
|
||||||
if (!controlOptions.HasFlag(ControlOptions.AutoRun))
|
if (!controlOptions.HasFlag(ControlOptions.AutoRun))
|
||||||
horizontal = Mathf.MoveTowards(horizontal, targetMoveX, inputGravity * deltaTime);
|
runtimeData.horizontal = Mathf.MoveTowards(runtimeData.horizontal, targetMoveX, inputGravity * deltaTime);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
horizontal = Mathf.MoveTowards(horizontal, targetMoveX, inputSensitivity * deltaTime);
|
runtimeData.horizontal = Mathf.MoveTowards(runtimeData.horizontal, targetMoveX, inputSensitivity * deltaTime);
|
||||||
|
|
||||||
if (targetMoveZ == 0f)
|
if (targetMoveZ == 0f)
|
||||||
{
|
{
|
||||||
if (!controlOptions.HasFlag(ControlOptions.AutoRun))
|
if (!controlOptions.HasFlag(ControlOptions.AutoRun))
|
||||||
vertical = Mathf.MoveTowards(vertical, targetMoveZ, inputGravity * deltaTime);
|
runtimeData.vertical = Mathf.MoveTowards(runtimeData.vertical, targetMoveZ, inputGravity * deltaTime);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
vertical = Mathf.MoveTowards(vertical, targetMoveZ, inputSensitivity * deltaTime);
|
runtimeData.vertical = Mathf.MoveTowards(runtimeData.vertical, targetMoveZ, inputSensitivity * deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplyMove(float deltaTime)
|
void ApplyMove(float deltaTime)
|
||||||
{
|
{
|
||||||
// Create initial direction vector without jumpSpeed (y-axis).
|
// Create initial direction vector without jumpSpeed (y-axis).
|
||||||
direction = new Vector3(horizontal, 0f, vertical);
|
runtimeData.direction = new Vector3(runtimeData.horizontal, 0f, runtimeData.vertical);
|
||||||
|
|
||||||
// Clamp so diagonal strafing isn't a speed advantage.
|
// Clamp so diagonal strafing isn't a speed advantage.
|
||||||
direction = Vector3.ClampMagnitude(direction, 1f);
|
runtimeData.direction = Vector3.ClampMagnitude(runtimeData.direction, 1f);
|
||||||
|
|
||||||
// Transforms direction from local space to world space.
|
// Transforms direction from local space to world space.
|
||||||
direction = transform.TransformDirection(direction);
|
runtimeData.direction = transform.TransformDirection(runtimeData.direction);
|
||||||
|
|
||||||
// Multiply for desired ground speed.
|
// Multiply for desired ground speed.
|
||||||
direction *= maxMoveSpeed;
|
runtimeData.direction *= maxMoveSpeed;
|
||||||
|
|
||||||
//// Add jumpSpeed to direction as last step.
|
|
||||||
//direction.y = jumpSpeed;
|
|
||||||
|
|
||||||
// Finally move the character.
|
// Finally move the character.
|
||||||
characterController.Move(direction * deltaTime);
|
characterController.Move(runtimeData.direction * deltaTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,7 +14,7 @@ public class PlayerControllerBase : NetworkBehaviour
|
|||||||
{
|
{
|
||||||
const float BASE_DPI = 96f;
|
const float BASE_DPI = 96f;
|
||||||
|
|
||||||
public enum GroundState : byte { Jumping, Falling, Grounded }
|
public enum GroundState : byte { Grounded, Jumping, Falling }
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public struct MoveKeys
|
public struct MoveKeys
|
||||||
@ -112,39 +112,102 @@ public enum ControlOptions : byte
|
|||||||
[Tooltip("Jump acceleration in meters per second squared")]
|
[Tooltip("Jump acceleration in meters per second squared")]
|
||||||
public float jumpAcceleration = 4f;
|
public float jumpAcceleration = 4f;
|
||||||
|
|
||||||
|
// Runtime data in a struct so it can be folded up in inspector
|
||||||
|
[Serializable]
|
||||||
|
public struct RuntimeData
|
||||||
|
{
|
||||||
|
[ReadOnly, SerializeField, Range(-1f, 1f)] float _horizontal;
|
||||||
|
[ReadOnly, SerializeField, Range(-1f, 1f)] float _vertical;
|
||||||
|
[ReadOnly, SerializeField, Range(-300f, 300f)] float _turnSpeed;
|
||||||
|
[ReadOnly, SerializeField, Range(-10f, 10f)] float _jumpSpeed;
|
||||||
|
[ReadOnly, SerializeField, Range(-1.5f, 1.5f)] float _animVelocity;
|
||||||
|
[ReadOnly, SerializeField, Range(-1.5f, 1.5f)] float _animRotation;
|
||||||
|
[ReadOnly, SerializeField, Range(-1f, 1f)] float _mouseInputX;
|
||||||
|
[ReadOnly, SerializeField, Range(0, 30f)] float _mouseSensitivity;
|
||||||
|
[ReadOnly, SerializeField] GroundState _groundState;
|
||||||
|
[ReadOnly, SerializeField] Vector3 _direction;
|
||||||
|
[ReadOnly, SerializeField] Vector3Int _velocity;
|
||||||
|
[ReadOnly, SerializeField] GameObject _controllerUI;
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
public float horizontal
|
||||||
|
{
|
||||||
|
get => _horizontal;
|
||||||
|
internal set => _horizontal = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float vertical
|
||||||
|
{
|
||||||
|
get => _vertical;
|
||||||
|
internal set => _vertical = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float turnSpeed
|
||||||
|
{
|
||||||
|
get => _turnSpeed;
|
||||||
|
internal set => _turnSpeed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float jumpSpeed
|
||||||
|
{
|
||||||
|
get => _jumpSpeed;
|
||||||
|
internal set => _jumpSpeed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float animVelocity
|
||||||
|
{
|
||||||
|
get => _animVelocity;
|
||||||
|
internal set => _animVelocity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float animRotation
|
||||||
|
{
|
||||||
|
get => _animRotation;
|
||||||
|
internal set => _animRotation = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float mouseInputX
|
||||||
|
{
|
||||||
|
get => _mouseInputX;
|
||||||
|
internal set => _mouseInputX = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float mouseSensitivity
|
||||||
|
{
|
||||||
|
get => _mouseSensitivity;
|
||||||
|
internal set => _mouseSensitivity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroundState groundState
|
||||||
|
{
|
||||||
|
get => _groundState;
|
||||||
|
internal set => _groundState = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 direction
|
||||||
|
{
|
||||||
|
get => _direction;
|
||||||
|
internal set => _direction = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3Int velocity
|
||||||
|
{
|
||||||
|
get => _velocity;
|
||||||
|
internal set => _velocity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameObject controllerUI
|
||||||
|
{
|
||||||
|
get => _controllerUI;
|
||||||
|
internal set => _controllerUI = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
[Header("Diagnostics")]
|
[Header("Diagnostics")]
|
||||||
[ReadOnly, SerializeField]
|
public RuntimeData runtimeData;
|
||||||
GroundState groundState = GroundState.Grounded;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField, Range(-1f, 1f)]
|
|
||||||
float horizontal;
|
|
||||||
[ReadOnly, SerializeField, Range(-1f, 1f)]
|
|
||||||
float vertical;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField, Range(-1f, 1f)]
|
|
||||||
float mouseInputX;
|
|
||||||
[ReadOnly, SerializeField, Range(0, 30f)]
|
|
||||||
float mouseSensitivity;
|
|
||||||
[ReadOnly, SerializeField, Range(-300f, 300f)]
|
|
||||||
float turnSpeed;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField, Range(-10f, 10f)]
|
|
||||||
float jumpSpeed;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField, Range(-1.5f, 1.5f)]
|
|
||||||
float animVelocity;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField, Range(-1.5f, 1.5f)]
|
|
||||||
float animRotation;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField]
|
|
||||||
Vector3 direction;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField]
|
|
||||||
Vector3Int velocity;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField]
|
|
||||||
GameObject controllerUI;
|
|
||||||
|
|
||||||
#region Network Setup
|
#region Network Setup
|
||||||
|
|
||||||
@ -187,17 +250,16 @@ void Reset()
|
|||||||
|
|
||||||
void OnDisable()
|
void OnDisable()
|
||||||
{
|
{
|
||||||
horizontal = 0f;
|
runtimeData.horizontal = 0f;
|
||||||
vertical = 0f;
|
runtimeData.vertical = 0f;
|
||||||
turnSpeed = 0f;
|
runtimeData.turnSpeed = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnStartAuthority()
|
public override void OnStartAuthority()
|
||||||
{
|
{
|
||||||
// Calculate DPI-aware sensitivity
|
// Calculate DPI-aware sensitivity
|
||||||
float dpiScale = (Screen.dpi > 0) ? (Screen.dpi / BASE_DPI) : 1f;
|
float dpiScale = (Screen.dpi > 0) ? (Screen.dpi / BASE_DPI) : 1f;
|
||||||
mouseSensitivity = turnAcceleration * dpiScale;
|
runtimeData.mouseSensitivity = turnAcceleration * dpiScale;
|
||||||
//Debug.Log($"Screen DPI: {Screen.dpi}, DPI Scale: {dpiScale}, Adjusted Turn Acceleration: {turnAccelerationDPI}");
|
|
||||||
|
|
||||||
SetCursor(controlOptions.HasFlag(ControlOptions.MouseSteer));
|
SetCursor(controlOptions.HasFlag(ControlOptions.MouseSteer));
|
||||||
|
|
||||||
@ -215,22 +277,22 @@ public override void OnStopAuthority()
|
|||||||
public override void OnStartLocalPlayer()
|
public override void OnStartLocalPlayer()
|
||||||
{
|
{
|
||||||
if (ControllerUIPrefab != null)
|
if (ControllerUIPrefab != null)
|
||||||
controllerUI = Instantiate(ControllerUIPrefab);
|
runtimeData.controllerUI = Instantiate(ControllerUIPrefab);
|
||||||
|
|
||||||
if (controllerUI != null)
|
if (runtimeData.controllerUI != null)
|
||||||
{
|
{
|
||||||
if (controllerUI.TryGetComponent(out PlayerControllerUI canvasControlPanel))
|
if (runtimeData.controllerUI.TryGetComponent(out PlayerControllerUI canvasControlPanel))
|
||||||
canvasControlPanel.Refresh(moveKeys, optionsKeys);
|
canvasControlPanel.Refresh(moveKeys, optionsKeys);
|
||||||
|
|
||||||
controllerUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
runtimeData.controllerUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnStopLocalPlayer()
|
public override void OnStopLocalPlayer()
|
||||||
{
|
{
|
||||||
if (controllerUI != null)
|
if (runtimeData.controllerUI != null)
|
||||||
Destroy(controllerUI);
|
Destroy(runtimeData.controllerUI);
|
||||||
controllerUI = null;
|
runtimeData.controllerUI = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -255,12 +317,12 @@ void Update()
|
|||||||
|
|
||||||
// Reset ground state
|
// Reset ground state
|
||||||
if (characterController.isGrounded)
|
if (characterController.isGrounded)
|
||||||
groundState = GroundState.Grounded;
|
runtimeData.groundState = GroundState.Grounded;
|
||||||
else if (groundState != GroundState.Jumping)
|
else if (runtimeData.groundState != GroundState.Jumping)
|
||||||
groundState = GroundState.Falling;
|
runtimeData.groundState = GroundState.Falling;
|
||||||
|
|
||||||
// Diagnostic velocity...FloorToInt for display purposes
|
// Diagnostic velocity...FloorToInt for display purposes
|
||||||
velocity = Vector3Int.FloorToInt(characterController.velocity);
|
runtimeData.velocity = Vector3Int.FloorToInt(characterController.velocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetCursor(bool locked)
|
void SetCursor(bool locked)
|
||||||
@ -284,8 +346,8 @@ void HandleOptions()
|
|||||||
{
|
{
|
||||||
controlOptions ^= ControlOptions.ShowUI;
|
controlOptions ^= ControlOptions.ShowUI;
|
||||||
|
|
||||||
if (controllerUI != null)
|
if (runtimeData.controllerUI != null)
|
||||||
controllerUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
runtimeData.controllerUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,62 +365,62 @@ void HandleTurning(float deltaTime)
|
|||||||
// If there's turn input or AutoRun is not enabled, adjust turn speed towards target
|
// If there's turn input or AutoRun is not enabled, adjust turn speed towards target
|
||||||
// If no turn input and AutoRun is enabled, maintain the previous turn speed
|
// If no turn input and AutoRun is enabled, maintain the previous turn speed
|
||||||
if (targetTurnSpeed != 0f || !controlOptions.HasFlag(ControlOptions.AutoRun))
|
if (targetTurnSpeed != 0f || !controlOptions.HasFlag(ControlOptions.AutoRun))
|
||||||
turnSpeed = Mathf.MoveTowards(turnSpeed, targetTurnSpeed, turnAcceleration * maxTurnSpeed * deltaTime);
|
runtimeData.turnSpeed = Mathf.MoveTowards(runtimeData.turnSpeed, targetTurnSpeed, turnAcceleration * maxTurnSpeed * deltaTime);
|
||||||
|
|
||||||
transform.Rotate(0f, turnSpeed * deltaTime, 0f);
|
transform.Rotate(0f, runtimeData.turnSpeed * deltaTime, 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleMouseSteer(float deltaTime)
|
void HandleMouseSteer(float deltaTime)
|
||||||
{
|
{
|
||||||
// Accumulate mouse input over time
|
// Accumulate mouse input over time
|
||||||
mouseInputX += Input.GetAxisRaw("Mouse X") * mouseSensitivity;
|
runtimeData.mouseInputX += Input.GetAxisRaw("Mouse X") * runtimeData.mouseSensitivity;
|
||||||
|
|
||||||
// Clamp the accumulator to simulate key press behavior
|
// Clamp the accumulator to simulate key press behavior
|
||||||
mouseInputX = Mathf.Clamp(mouseInputX, -1f, 1f);
|
runtimeData.mouseInputX = Mathf.Clamp(runtimeData.mouseInputX, -1f, 1f);
|
||||||
|
|
||||||
// Calculate target turn speed
|
// Calculate target turn speed
|
||||||
float targetTurnSpeed = mouseInputX * maxTurnSpeed;
|
float targetTurnSpeed = runtimeData.mouseInputX * maxTurnSpeed;
|
||||||
|
|
||||||
// Use the same acceleration logic as HandleTurning
|
// Use the same acceleration logic as HandleTurning
|
||||||
turnSpeed = Mathf.MoveTowards(turnSpeed, targetTurnSpeed, mouseSensitivity * maxTurnSpeed * deltaTime);
|
runtimeData.turnSpeed = Mathf.MoveTowards(runtimeData.turnSpeed, targetTurnSpeed, runtimeData.mouseSensitivity * maxTurnSpeed * deltaTime);
|
||||||
|
|
||||||
// Apply rotation
|
// Apply rotation
|
||||||
transform.Rotate(0f, turnSpeed * deltaTime, 0f);
|
transform.Rotate(0f, runtimeData.turnSpeed * deltaTime, 0f);
|
||||||
|
|
||||||
mouseInputX = Mathf.MoveTowards(mouseInputX, 0f, mouseSensitivity * deltaTime);
|
runtimeData.mouseInputX = Mathf.MoveTowards(runtimeData.mouseInputX, 0f, runtimeData.mouseSensitivity * deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleJumping(float deltaTime)
|
void HandleJumping(float deltaTime)
|
||||||
{
|
{
|
||||||
if (groundState != GroundState.Falling && moveKeys.Jump != KeyCode.None && Input.GetKey(moveKeys.Jump))
|
if (runtimeData.groundState != GroundState.Falling && moveKeys.Jump != KeyCode.None && Input.GetKey(moveKeys.Jump))
|
||||||
{
|
{
|
||||||
if (groundState != GroundState.Jumping)
|
if (runtimeData.groundState != GroundState.Jumping)
|
||||||
{
|
{
|
||||||
groundState = GroundState.Jumping;
|
runtimeData.groundState = GroundState.Jumping;
|
||||||
jumpSpeed = initialJumpSpeed;
|
runtimeData.jumpSpeed = initialJumpSpeed;
|
||||||
}
|
}
|
||||||
else if (jumpSpeed < maxJumpSpeed)
|
else if (runtimeData.jumpSpeed < maxJumpSpeed)
|
||||||
{
|
{
|
||||||
// Increase jumpSpeed using a square root function for a fast start and slow finish
|
// Increase jumpSpeed using a square root function for a fast start and slow finish
|
||||||
float jumpProgress = (jumpSpeed - initialJumpSpeed) / (maxJumpSpeed - initialJumpSpeed);
|
float jumpProgress = (runtimeData.jumpSpeed - initialJumpSpeed) / (maxJumpSpeed - initialJumpSpeed);
|
||||||
jumpSpeed += (jumpAcceleration * Mathf.Sqrt(1 - jumpProgress)) * deltaTime;
|
runtimeData.jumpSpeed += (jumpAcceleration * Mathf.Sqrt(1 - jumpProgress)) * deltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jumpSpeed >= maxJumpSpeed)
|
if (runtimeData.jumpSpeed >= maxJumpSpeed)
|
||||||
{
|
{
|
||||||
jumpSpeed = maxJumpSpeed;
|
runtimeData.jumpSpeed = maxJumpSpeed;
|
||||||
groundState = GroundState.Falling;
|
runtimeData.groundState = GroundState.Falling;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (groundState != GroundState.Grounded)
|
else if (runtimeData.groundState != GroundState.Grounded)
|
||||||
{
|
{
|
||||||
groundState = GroundState.Falling;
|
runtimeData.groundState = GroundState.Falling;
|
||||||
jumpSpeed = Mathf.Min(jumpSpeed, maxJumpSpeed);
|
runtimeData.jumpSpeed = Mathf.Min(runtimeData.jumpSpeed, maxJumpSpeed);
|
||||||
jumpSpeed += Physics.gravity.y * deltaTime;
|
runtimeData.jumpSpeed += Physics.gravity.y * deltaTime;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
// maintain small downward speed for when falling off ledges
|
// maintain small downward speed for when falling off ledges
|
||||||
jumpSpeed = Physics.gravity.y * deltaTime;
|
runtimeData.jumpSpeed = Physics.gravity.y * deltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleMove(float deltaTime)
|
void HandleMove(float deltaTime)
|
||||||
@ -376,39 +438,40 @@ void HandleMove(float deltaTime)
|
|||||||
if (targetMoveX == 0f)
|
if (targetMoveX == 0f)
|
||||||
{
|
{
|
||||||
if (!controlOptions.HasFlag(ControlOptions.AutoRun))
|
if (!controlOptions.HasFlag(ControlOptions.AutoRun))
|
||||||
horizontal = Mathf.MoveTowards(horizontal, targetMoveX, inputGravity * deltaTime);
|
runtimeData.horizontal = Mathf.MoveTowards(runtimeData.horizontal, targetMoveX, inputGravity * deltaTime);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
horizontal = Mathf.MoveTowards(horizontal, targetMoveX, inputSensitivity * deltaTime);
|
runtimeData.horizontal = Mathf.MoveTowards(runtimeData.horizontal, targetMoveX, inputSensitivity * deltaTime);
|
||||||
|
|
||||||
if (targetMoveZ == 0f)
|
if (targetMoveZ == 0f)
|
||||||
{
|
{
|
||||||
if (!controlOptions.HasFlag(ControlOptions.AutoRun))
|
if (!controlOptions.HasFlag(ControlOptions.AutoRun))
|
||||||
vertical = Mathf.MoveTowards(vertical, targetMoveZ, inputGravity * deltaTime);
|
runtimeData.vertical = Mathf.MoveTowards(runtimeData.vertical, targetMoveZ, inputGravity * deltaTime);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
vertical = Mathf.MoveTowards(vertical, targetMoveZ, inputSensitivity * deltaTime);
|
runtimeData.vertical = Mathf.MoveTowards(runtimeData.vertical, targetMoveZ, inputSensitivity * deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplyMove(float deltaTime)
|
void ApplyMove(float deltaTime)
|
||||||
{
|
{
|
||||||
// Create initial direction vector without jumpSpeed (y-axis).
|
// Create initial direction vector without jumpSpeed (y-axis).
|
||||||
direction = new Vector3(horizontal, 0f, vertical);
|
runtimeData.direction = new Vector3(runtimeData.horizontal, 0f, runtimeData.vertical);
|
||||||
|
|
||||||
// Clamp so diagonal strafing isn't a speed advantage.
|
// Clamp so diagonal strafing isn't a speed advantage.
|
||||||
direction = Vector3.ClampMagnitude(direction, 1f);
|
runtimeData.direction = Vector3.ClampMagnitude(runtimeData.direction, 1f);
|
||||||
|
|
||||||
// Transforms direction from local space to world space.
|
// Transforms direction from local space to world space.
|
||||||
direction = transform.TransformDirection(direction);
|
runtimeData.direction = transform.TransformDirection(runtimeData.direction);
|
||||||
|
|
||||||
// Multiply for desired ground speed.
|
// Multiply for desired ground speed.
|
||||||
direction *= maxMoveSpeed;
|
runtimeData.direction *= maxMoveSpeed;
|
||||||
|
|
||||||
// Add jumpSpeed to direction as last step.
|
// Add jumpSpeed to direction as last step.
|
||||||
direction.y = jumpSpeed;
|
//runtimeData.direction.y = runtimeData.jumpSpeed;
|
||||||
|
runtimeData.direction = new Vector3(runtimeData.direction.x, runtimeData.jumpSpeed, runtimeData.direction.z);
|
||||||
|
|
||||||
// Finally move the character.
|
// Finally move the character.
|
||||||
characterController.Move(direction * deltaTime);
|
characterController.Move(runtimeData.direction * deltaTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ namespace Mirror.Examples.Common.Controllers.Tank
|
|||||||
[DisallowMultipleComponent]
|
[DisallowMultipleComponent]
|
||||||
public class TankControllerBase : NetworkBehaviour
|
public class TankControllerBase : NetworkBehaviour
|
||||||
{
|
{
|
||||||
public enum GroundState : byte { Jumping, Falling, Grounded }
|
public enum GroundState : byte { Grounded, Jumping, Falling }
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public struct MoveKeys
|
public struct MoveKeys
|
||||||
@ -90,32 +90,81 @@ public enum ControlOptions : byte
|
|||||||
[Tooltip("Rotation acceleration in degrees per second squared")]
|
[Tooltip("Rotation acceleration in degrees per second squared")]
|
||||||
public float turnAcceleration = 3f;
|
public float turnAcceleration = 3f;
|
||||||
|
|
||||||
|
// Runtime data in a struct so it can be folded up in inspector
|
||||||
|
[Serializable]
|
||||||
|
public struct RuntimeData
|
||||||
|
{
|
||||||
|
[ReadOnly, SerializeField, Range(-1f, 1f)] float _horizontal;
|
||||||
|
[ReadOnly, SerializeField, Range(-1f, 1f)] float _vertical;
|
||||||
|
[ReadOnly, SerializeField, Range(-300f, 300f)] float _turnSpeed;
|
||||||
|
[ReadOnly, SerializeField, Range(-1.5f, 1.5f)] float _animVelocity;
|
||||||
|
[ReadOnly, SerializeField, Range(-1.5f, 1.5f)] float _animRotation;
|
||||||
|
[ReadOnly, SerializeField] GroundState _groundState;
|
||||||
|
[ReadOnly, SerializeField] Vector3 _direction;
|
||||||
|
[ReadOnly, SerializeField] Vector3Int _velocity;
|
||||||
|
[ReadOnly, SerializeField] GameObject _controllerUI;
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
public float horizontal
|
||||||
|
{
|
||||||
|
get => _horizontal;
|
||||||
|
internal set => _horizontal = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float vertical
|
||||||
|
{
|
||||||
|
get => _vertical;
|
||||||
|
internal set => _vertical = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float turnSpeed
|
||||||
|
{
|
||||||
|
get => _turnSpeed;
|
||||||
|
internal set => _turnSpeed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float animVelocity
|
||||||
|
{
|
||||||
|
get => _animVelocity;
|
||||||
|
internal set => _animVelocity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float animRotation
|
||||||
|
{
|
||||||
|
get => _animRotation;
|
||||||
|
internal set => _animRotation = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameObject controllerUI
|
||||||
|
{
|
||||||
|
get => _controllerUI;
|
||||||
|
internal set => _controllerUI = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 direction
|
||||||
|
{
|
||||||
|
get => _direction;
|
||||||
|
internal set => _direction = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3Int velocity
|
||||||
|
{
|
||||||
|
get => _velocity;
|
||||||
|
internal set => _velocity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroundState groundState
|
||||||
|
{
|
||||||
|
get => _groundState;
|
||||||
|
internal set => _groundState = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
[Header("Diagnostics")]
|
[Header("Diagnostics")]
|
||||||
[ReadOnly, SerializeField]
|
public RuntimeData runtimeData;
|
||||||
GroundState groundState = GroundState.Grounded;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField, Range(-1f, 1f)]
|
|
||||||
float horizontal;
|
|
||||||
[ReadOnly, SerializeField, Range(-1f, 1f)]
|
|
||||||
float vertical;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField, Range(-300f, 300f)]
|
|
||||||
float turnSpeed;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField, Range(-1.5f, 1.5f)]
|
|
||||||
float animVelocity;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField, Range(-1.5f, 1.5f)]
|
|
||||||
float animRotation;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField]
|
|
||||||
Vector3 direction;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField]
|
|
||||||
Vector3Int velocity;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField]
|
|
||||||
GameObject controllerUI;
|
|
||||||
|
|
||||||
#region Network Setup
|
#region Network Setup
|
||||||
|
|
||||||
@ -164,15 +213,13 @@ protected virtual void Reset()
|
|||||||
|
|
||||||
void OnDisable()
|
void OnDisable()
|
||||||
{
|
{
|
||||||
horizontal = 0f;
|
runtimeData.horizontal = 0f;
|
||||||
vertical = 0f;
|
runtimeData.vertical = 0f;
|
||||||
turnSpeed = 0f;
|
runtimeData.turnSpeed = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnStartAuthority()
|
public override void OnStartAuthority()
|
||||||
{
|
{
|
||||||
// capsuleCollider and characterController are mutually exclusive
|
|
||||||
// Having both enabled would double fire triggers and other collisions
|
|
||||||
characterController.enabled = true;
|
characterController.enabled = true;
|
||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
}
|
}
|
||||||
@ -180,31 +227,28 @@ public override void OnStartAuthority()
|
|||||||
public override void OnStopAuthority()
|
public override void OnStopAuthority()
|
||||||
{
|
{
|
||||||
this.enabled = false;
|
this.enabled = false;
|
||||||
|
|
||||||
// capsuleCollider and characterController are mutually exclusive
|
|
||||||
// Having both enabled would double fire triggers and other collisions
|
|
||||||
characterController.enabled = false;
|
characterController.enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnStartLocalPlayer()
|
public override void OnStartLocalPlayer()
|
||||||
{
|
{
|
||||||
if (ControllerUIPrefab != null)
|
if (ControllerUIPrefab != null)
|
||||||
controllerUI = Instantiate(ControllerUIPrefab);
|
runtimeData.controllerUI = Instantiate(ControllerUIPrefab);
|
||||||
|
|
||||||
if (controllerUI != null)
|
if (runtimeData.controllerUI != null)
|
||||||
{
|
{
|
||||||
if (controllerUI.TryGetComponent(out TankControllerUI canvasControlPanel))
|
if (runtimeData.controllerUI.TryGetComponent(out TankControllerUI canvasControlPanel))
|
||||||
canvasControlPanel.Refresh(moveKeys, optionsKeys);
|
canvasControlPanel.Refresh(moveKeys, optionsKeys);
|
||||||
|
|
||||||
controllerUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
runtimeData.controllerUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnStopLocalPlayer()
|
public override void OnStopLocalPlayer()
|
||||||
{
|
{
|
||||||
if (controllerUI != null)
|
if (runtimeData.controllerUI != null)
|
||||||
Destroy(controllerUI);
|
Destroy(runtimeData.controllerUI);
|
||||||
controllerUI = null;
|
runtimeData.controllerUI = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -223,12 +267,12 @@ void Update()
|
|||||||
|
|
||||||
// Reset ground state
|
// Reset ground state
|
||||||
if (characterController.isGrounded)
|
if (characterController.isGrounded)
|
||||||
groundState = GroundState.Grounded;
|
runtimeData.groundState = GroundState.Grounded;
|
||||||
else if (groundState != GroundState.Jumping)
|
else if (runtimeData.groundState != GroundState.Jumping)
|
||||||
groundState = GroundState.Falling;
|
runtimeData.groundState = GroundState.Falling;
|
||||||
|
|
||||||
// Diagnostic velocity...FloorToInt for display purposes
|
// Diagnostic velocity...FloorToInt for display purposes
|
||||||
velocity = Vector3Int.FloorToInt(characterController.velocity);
|
runtimeData.velocity = Vector3Int.FloorToInt(characterController.velocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleOptions()
|
void HandleOptions()
|
||||||
@ -240,8 +284,8 @@ void HandleOptions()
|
|||||||
{
|
{
|
||||||
controlOptions ^= ControlOptions.ShowUI;
|
controlOptions ^= ControlOptions.ShowUI;
|
||||||
|
|
||||||
if (controllerUI != null)
|
if (runtimeData.controllerUI != null)
|
||||||
controllerUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
runtimeData.controllerUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,9 +303,9 @@ void HandleTurning(float deltaTime)
|
|||||||
// If there's turn input or AutoRun is not enabled, adjust turn speed towards target
|
// If there's turn input or AutoRun is not enabled, adjust turn speed towards target
|
||||||
// If no turn input and AutoRun is enabled, maintain the previous turn speed
|
// If no turn input and AutoRun is enabled, maintain the previous turn speed
|
||||||
if (targetTurnSpeed != 0f || !controlOptions.HasFlag(ControlOptions.AutoRun))
|
if (targetTurnSpeed != 0f || !controlOptions.HasFlag(ControlOptions.AutoRun))
|
||||||
turnSpeed = Mathf.MoveTowards(turnSpeed, targetTurnSpeed, turnAcceleration * maxTurnSpeed * deltaTime);
|
runtimeData.turnSpeed = Mathf.MoveTowards(runtimeData.turnSpeed, targetTurnSpeed, turnAcceleration * maxTurnSpeed * deltaTime);
|
||||||
|
|
||||||
transform.Rotate(0f, turnSpeed * deltaTime, 0f);
|
transform.Rotate(0f, runtimeData.turnSpeed * deltaTime, 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleMove(float deltaTime)
|
void HandleMove(float deltaTime)
|
||||||
@ -277,39 +321,39 @@ void HandleMove(float deltaTime)
|
|||||||
if (targetMoveX == 0f)
|
if (targetMoveX == 0f)
|
||||||
{
|
{
|
||||||
if (!controlOptions.HasFlag(ControlOptions.AutoRun))
|
if (!controlOptions.HasFlag(ControlOptions.AutoRun))
|
||||||
horizontal = Mathf.MoveTowards(horizontal, targetMoveX, inputGravity * deltaTime);
|
runtimeData.horizontal = Mathf.MoveTowards(runtimeData.horizontal, targetMoveX, inputGravity * deltaTime);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
horizontal = Mathf.MoveTowards(horizontal, targetMoveX, inputSensitivity * deltaTime);
|
runtimeData.horizontal = Mathf.MoveTowards(runtimeData.horizontal, targetMoveX, inputSensitivity * deltaTime);
|
||||||
|
|
||||||
if (targetMoveZ == 0f)
|
if (targetMoveZ == 0f)
|
||||||
{
|
{
|
||||||
if (!controlOptions.HasFlag(ControlOptions.AutoRun))
|
if (!controlOptions.HasFlag(ControlOptions.AutoRun))
|
||||||
vertical = Mathf.MoveTowards(vertical, targetMoveZ, inputGravity * deltaTime);
|
runtimeData.vertical = Mathf.MoveTowards(runtimeData.vertical, targetMoveZ, inputGravity * deltaTime);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
vertical = Mathf.MoveTowards(vertical, targetMoveZ, inputSensitivity * deltaTime);
|
runtimeData.vertical = Mathf.MoveTowards(runtimeData.vertical, targetMoveZ, inputSensitivity * deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplyMove(float deltaTime)
|
void ApplyMove(float deltaTime)
|
||||||
{
|
{
|
||||||
// Create initial direction vector without jumpSpeed (y-axis).
|
// Create initial direction vector without jumpSpeed (y-axis).
|
||||||
direction = new Vector3(horizontal, 0f, vertical);
|
runtimeData.direction = new Vector3(runtimeData.horizontal, 0f, runtimeData.vertical);
|
||||||
|
|
||||||
// Clamp so diagonal strafing isn't a speed advantage.
|
// Clamp so diagonal strafing isn't a speed advantage.
|
||||||
direction = Vector3.ClampMagnitude(direction, 1f);
|
runtimeData.direction = Vector3.ClampMagnitude(runtimeData.direction, 1f);
|
||||||
|
|
||||||
// Transforms direction from local space to world space.
|
// Transforms direction from local space to world space.
|
||||||
direction = transform.TransformDirection(direction);
|
runtimeData.direction = transform.TransformDirection(runtimeData.direction);
|
||||||
|
|
||||||
// Multiply for desired ground speed.
|
// Multiply for desired ground speed.
|
||||||
direction *= maxMoveSpeed;
|
runtimeData.direction *= maxMoveSpeed;
|
||||||
|
|
||||||
// Add gravity in case we drove off a cliff.
|
// Add gravity in case we drove off a cliff.
|
||||||
direction += Physics.gravity;
|
runtimeData.direction += Physics.gravity;
|
||||||
|
|
||||||
// Finally move the character.
|
// Finally move the character.
|
||||||
characterController.Move(direction * deltaTime);
|
characterController.Move(runtimeData.direction * deltaTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ namespace Mirror.Examples.Common.Controllers.Tank
|
|||||||
[DisallowMultipleComponent]
|
[DisallowMultipleComponent]
|
||||||
public class TankTurretBase : NetworkBehaviour
|
public class TankTurretBase : NetworkBehaviour
|
||||||
{
|
{
|
||||||
|
const float BASE_DPI = 96f;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public struct OptionsKeys
|
public struct OptionsKeys
|
||||||
{
|
{
|
||||||
@ -31,8 +33,6 @@ public struct OtherKeys
|
|||||||
public KeyCode Shoot;
|
public KeyCode Shoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float BASE_DPI = 96f;
|
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum ControlOptions : byte
|
public enum ControlOptions : byte
|
||||||
{
|
{
|
||||||
@ -98,7 +98,7 @@ public enum ControlOptions : byte
|
|||||||
[Range(0, 300f)]
|
[Range(0, 300f)]
|
||||||
[Tooltip("Max Rotation in degrees per second")]
|
[Tooltip("Max Rotation in degrees per second")]
|
||||||
public float maxTurretSpeed = 250f;
|
public float maxTurretSpeed = 250f;
|
||||||
[Range(0, 10f)]
|
[Range(0, 30f)]
|
||||||
[Tooltip("Rotation acceleration in degrees per second squared")]
|
[Tooltip("Rotation acceleration in degrees per second squared")]
|
||||||
public float turretAcceleration = 10f;
|
public float turretAcceleration = 10f;
|
||||||
|
|
||||||
@ -116,33 +116,69 @@ public enum ControlOptions : byte
|
|||||||
[Tooltip("Pitch acceleration in degrees per second squared")]
|
[Tooltip("Pitch acceleration in degrees per second squared")]
|
||||||
public float pitchAcceleration = 3f;
|
public float pitchAcceleration = 3f;
|
||||||
|
|
||||||
[Header("Diagnostics")]
|
// Runtime data in a struct so it can be folded up in inspector
|
||||||
[ReadOnly, SerializeField, Range(-1f, 1f)]
|
[Serializable]
|
||||||
float mouseInputX;
|
public struct RuntimeData
|
||||||
[ReadOnly, SerializeField, Range(0, 30f)]
|
|
||||||
float mouseSensitivity;
|
|
||||||
[ReadOnly, SerializeField, Range(-300f, 300f)]
|
|
||||||
float turretSpeed;
|
|
||||||
[ReadOnly, SerializeField, Range(-180f, 180f)]
|
|
||||||
float pitchAngle;
|
|
||||||
[ReadOnly, SerializeField, Range(-180f, 180f)]
|
|
||||||
float pitchSpeed;
|
|
||||||
[ReadOnly, SerializeField]
|
|
||||||
double lastShotTime;
|
|
||||||
|
|
||||||
[ReadOnly, SerializeField]
|
|
||||||
GameObject turretUI;
|
|
||||||
|
|
||||||
void OnPlayerColorChanged(Color32 _, Color32 newColor)
|
|
||||||
{
|
{
|
||||||
if (cachedMaterial == null)
|
[ReadOnly, SerializeField, Range(-300f, 300f)] float _turretSpeed;
|
||||||
cachedMaterial = playerObject.GetComponent<Renderer>().material;
|
[ReadOnly, SerializeField, Range(-180f, 180f)] float _pitchAngle;
|
||||||
|
[ReadOnly, SerializeField, Range(-180f, 180f)] float _pitchSpeed;
|
||||||
|
[ReadOnly, SerializeField, Range(-1f, 1f)] float _mouseInputX;
|
||||||
|
[ReadOnly, SerializeField, Range(0, 30f)] float _mouseSensitivity;
|
||||||
|
[ReadOnly, SerializeField] double _lastShotTime;
|
||||||
|
[ReadOnly, SerializeField] GameObject _turretUI;
|
||||||
|
|
||||||
cachedMaterial.color = newColor;
|
#region Properties
|
||||||
playerObject.SetActive(newColor != Color.black);
|
|
||||||
|
public float mouseInputX
|
||||||
|
{
|
||||||
|
get => _mouseInputX;
|
||||||
|
internal set => _mouseInputX = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float mouseSensitivity
|
||||||
|
{
|
||||||
|
get => _mouseSensitivity;
|
||||||
|
internal set => _mouseSensitivity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float turretSpeed
|
||||||
|
{
|
||||||
|
get => _turretSpeed;
|
||||||
|
internal set => _turretSpeed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float pitchAngle
|
||||||
|
{
|
||||||
|
get => _pitchAngle;
|
||||||
|
internal set => _pitchAngle = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float pitchSpeed
|
||||||
|
{
|
||||||
|
get => _pitchSpeed;
|
||||||
|
internal set => _pitchSpeed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double lastShotTime
|
||||||
|
{
|
||||||
|
get => _lastShotTime;
|
||||||
|
internal set => _lastShotTime = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameObject turretUI
|
||||||
|
{
|
||||||
|
get => _turretUI;
|
||||||
|
internal set => _turretUI = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Unity Callbacks
|
[Header("Diagnostics")]
|
||||||
|
public RuntimeData runtimeData;
|
||||||
|
|
||||||
|
#region Network Setup
|
||||||
|
|
||||||
protected override void OnValidate()
|
protected override void OnValidate()
|
||||||
{
|
{
|
||||||
@ -160,7 +196,7 @@ protected virtual void Reset()
|
|||||||
animator = GetComponentInChildren<Animator>();
|
animator = GetComponentInChildren<Animator>();
|
||||||
|
|
||||||
// Set default...this may be modified based on DPI at runtime
|
// Set default...this may be modified based on DPI at runtime
|
||||||
mouseSensitivity = turretAcceleration;
|
runtimeData.mouseSensitivity = turretAcceleration;
|
||||||
|
|
||||||
// Do a recursive search for a children named "Turret" and "ProjectileMount".
|
// Do a recursive search for a children named "Turret" and "ProjectileMount".
|
||||||
// They will be several levels deep in the hierarchy.
|
// They will be several levels deep in the hierarchy.
|
||||||
@ -215,6 +251,43 @@ Transform FindDeepChild(Transform aParent, string aName)
|
|||||||
this.enabled = false;
|
this.enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void OnStartLocalPlayer()
|
||||||
|
{
|
||||||
|
if (turretUIPrefab != null)
|
||||||
|
runtimeData.turretUI = Instantiate(turretUIPrefab);
|
||||||
|
|
||||||
|
if (runtimeData.turretUI != null)
|
||||||
|
{
|
||||||
|
if (runtimeData.turretUI.TryGetComponent(out TurretUI canvasControlPanel))
|
||||||
|
canvasControlPanel.Refresh(moveKeys, optionsKeys);
|
||||||
|
|
||||||
|
runtimeData.turretUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnStopLocalPlayer()
|
||||||
|
{
|
||||||
|
if (runtimeData.turretUI != null)
|
||||||
|
Destroy(runtimeData.turretUI);
|
||||||
|
runtimeData.turretUI = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnStartAuthority()
|
||||||
|
{
|
||||||
|
// Calculate DPI-aware sensitivity
|
||||||
|
float dpiScale = (Screen.dpi > 0) ? (Screen.dpi / BASE_DPI) : 1f;
|
||||||
|
runtimeData.mouseSensitivity = turretAcceleration * dpiScale;
|
||||||
|
|
||||||
|
SetCursor(controlOptions.HasFlag(ControlOptions.MouseLock));
|
||||||
|
this.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnStopAuthority()
|
||||||
|
{
|
||||||
|
SetCursor(false);
|
||||||
|
this.enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
@ -232,6 +305,15 @@ void Update()
|
|||||||
HandleShooting();
|
HandleShooting();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OnPlayerColorChanged(Color32 _, Color32 newColor)
|
||||||
|
{
|
||||||
|
if (cachedMaterial == null)
|
||||||
|
cachedMaterial = playerObject.GetComponent<Renderer>().material;
|
||||||
|
|
||||||
|
cachedMaterial.color = newColor;
|
||||||
|
playerObject.SetActive(newColor != Color.black);
|
||||||
|
}
|
||||||
|
|
||||||
void SetCursor(bool locked)
|
void SetCursor(bool locked)
|
||||||
{
|
{
|
||||||
Cursor.lockState = locked ? CursorLockMode.Locked : CursorLockMode.None;
|
Cursor.lockState = locked ? CursorLockMode.Locked : CursorLockMode.None;
|
||||||
@ -253,8 +335,8 @@ void HandleOptions()
|
|||||||
{
|
{
|
||||||
controlOptions ^= ControlOptions.ShowUI;
|
controlOptions ^= ControlOptions.ShowUI;
|
||||||
|
|
||||||
if (turretUI != null)
|
if (runtimeData.turretUI != null)
|
||||||
turretUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
runtimeData.turretUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,28 +350,28 @@ void HandleTurning(float deltaTime)
|
|||||||
if (moveKeys.TurnRight != KeyCode.None && Input.GetKey(moveKeys.TurnRight))
|
if (moveKeys.TurnRight != KeyCode.None && Input.GetKey(moveKeys.TurnRight))
|
||||||
targetTurnSpeed += maxTurretSpeed;
|
targetTurnSpeed += maxTurretSpeed;
|
||||||
|
|
||||||
turretSpeed = Mathf.MoveTowards(turretSpeed, targetTurnSpeed, turretAcceleration * maxTurretSpeed * deltaTime);
|
runtimeData.turretSpeed = Mathf.MoveTowards(runtimeData.turretSpeed, targetTurnSpeed, turretAcceleration * maxTurretSpeed * deltaTime);
|
||||||
turret.Rotate(0f, turretSpeed * deltaTime, 0f);
|
turret.Rotate(0f, runtimeData.turretSpeed * deltaTime, 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleMouseTurret(float deltaTime)
|
void HandleMouseTurret(float deltaTime)
|
||||||
{
|
{
|
||||||
// Accumulate mouse input over time
|
// Accumulate mouse input over time
|
||||||
mouseInputX += Input.GetAxisRaw("Mouse X") * mouseSensitivity;
|
runtimeData.mouseInputX += Input.GetAxisRaw("Mouse X") * runtimeData.mouseSensitivity;
|
||||||
|
|
||||||
// Clamp the accumulator to simulate key press behavior
|
// Clamp the accumulator to simulate key press behavior
|
||||||
mouseInputX = Mathf.Clamp(mouseInputX, -1f, 1f);
|
runtimeData.mouseInputX = Mathf.Clamp(runtimeData.mouseInputX, -1f, 1f);
|
||||||
|
|
||||||
// Calculate target turn speed
|
// Calculate target turn speed
|
||||||
float targetTurnSpeed = mouseInputX * maxTurretSpeed;
|
float targetTurnSpeed = runtimeData.mouseInputX * maxTurretSpeed;
|
||||||
|
|
||||||
// Use the same acceleration logic as HandleTurning
|
// Use the same acceleration logic as HandleTurning
|
||||||
turretSpeed = Mathf.MoveTowards(turretSpeed, targetTurnSpeed, mouseSensitivity * maxTurretSpeed * deltaTime);
|
runtimeData.turretSpeed = Mathf.MoveTowards(runtimeData.turretSpeed, targetTurnSpeed, runtimeData.mouseSensitivity * maxTurretSpeed * deltaTime);
|
||||||
|
|
||||||
// Apply rotation
|
// Apply rotation
|
||||||
turret.Rotate(0f, turretSpeed * deltaTime, 0f);
|
turret.Rotate(0f, runtimeData.turretSpeed * deltaTime, 0f);
|
||||||
|
|
||||||
mouseInputX = Mathf.MoveTowards(mouseInputX, 0f, mouseSensitivity * deltaTime);
|
runtimeData.mouseInputX = Mathf.MoveTowards(runtimeData.mouseInputX, 0f, runtimeData.mouseSensitivity * deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandlePitch(float deltaTime)
|
void HandlePitch(float deltaTime)
|
||||||
@ -310,19 +392,19 @@ void HandlePitch(float deltaTime)
|
|||||||
inputDetected = true;
|
inputDetected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pitchSpeed = Mathf.MoveTowards(pitchSpeed, targetPitchSpeed, pitchAcceleration * maxPitchSpeed * deltaTime);
|
runtimeData.pitchSpeed = Mathf.MoveTowards(runtimeData.pitchSpeed, targetPitchSpeed, pitchAcceleration * maxPitchSpeed * deltaTime);
|
||||||
|
|
||||||
// Apply pitch rotation
|
// Apply pitch rotation
|
||||||
pitchAngle += pitchSpeed * deltaTime;
|
runtimeData.pitchAngle += runtimeData.pitchSpeed * deltaTime;
|
||||||
pitchAngle = Mathf.Clamp(pitchAngle, -maxPitchUpAngle, maxPitchDownAngle);
|
runtimeData.pitchAngle = Mathf.Clamp(runtimeData.pitchAngle, -maxPitchUpAngle, maxPitchDownAngle);
|
||||||
|
|
||||||
// Return to -90 when no input
|
// Return to -90 when no input
|
||||||
if (!inputDetected && controlOptions.HasFlag(ControlOptions.AutoLevel))
|
if (!inputDetected && controlOptions.HasFlag(ControlOptions.AutoLevel))
|
||||||
pitchAngle = Mathf.MoveTowards(pitchAngle, 0f, maxPitchSpeed * deltaTime);
|
runtimeData.pitchAngle = Mathf.MoveTowards(runtimeData.pitchAngle, 0f, maxPitchSpeed * deltaTime);
|
||||||
|
|
||||||
// Apply rotation to barrel -- rotation is (-90, 0, 180) in the prefab
|
// Apply rotation to barrel -- rotation is (-90, 0, 180) in the prefab
|
||||||
// so that's what we have to work towards.
|
// so that's what we have to work towards.
|
||||||
barrel.localRotation = Quaternion.Euler(-90f + pitchAngle, 0f, 180f);
|
barrel.localRotation = Quaternion.Euler(-90f + runtimeData.pitchAngle, 0f, 180f);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Shooting
|
#region Shooting
|
||||||
@ -336,7 +418,7 @@ void HandleShooting()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanShoot => NetworkTime.time >= lastShotTime + cooldownTime;
|
bool CanShoot => NetworkTime.time >= runtimeData.lastShotTime + cooldownTime;
|
||||||
|
|
||||||
[Command]
|
[Command]
|
||||||
void CmdShoot()
|
void CmdShoot()
|
||||||
@ -358,13 +440,13 @@ void RpcShoot()
|
|||||||
// This has multiple callers in different contexts...don't consolidate, even if it looks like you could.
|
// This has multiple callers in different contexts...don't consolidate, even if it looks like you could.
|
||||||
void DoShoot()
|
void DoShoot()
|
||||||
{
|
{
|
||||||
//Debug.Log($"DoShoot isServerOnly:{isServerOnly} | isServer:{isServer} | isClient:{isClient}");
|
//Debug.Log($"DoShoot isServerOnly:{isServerOnly} | isServer:{isServer} | isClientOnly:{isClientOnly}");
|
||||||
|
|
||||||
|
// ProjectileMount.transform.parent.parent is the Barrel object with the Collider
|
||||||
// Turret
|
// Turret
|
||||||
// - Barrel (with Collider)
|
// - Barrel (with Collider)
|
||||||
// - BarrelEnd
|
// - BarrelEnd
|
||||||
// - ProjectileMount
|
// - ProjectileMount
|
||||||
// projectileMount.transform.parent.parent is the Barrel object with the Collider
|
|
||||||
|
|
||||||
if (isServerOnly)
|
if (isServerOnly)
|
||||||
{
|
{
|
||||||
@ -373,7 +455,7 @@ void DoShoot()
|
|||||||
Physics.IgnoreCollision(go.GetComponent<Collider>(), projectileMount.transform.parent.parent.GetComponent<Collider>());
|
Physics.IgnoreCollision(go.GetComponent<Collider>(), projectileMount.transform.parent.parent.GetComponent<Collider>());
|
||||||
|
|
||||||
// Update the last shot time
|
// Update the last shot time
|
||||||
lastShotTime = NetworkTime.time;
|
runtimeData.lastShotTime = NetworkTime.time;
|
||||||
}
|
}
|
||||||
else if (isServer)
|
else if (isServer)
|
||||||
{
|
{
|
||||||
@ -383,7 +465,7 @@ void DoShoot()
|
|||||||
Physics.IgnoreCollision(go.GetComponent<Collider>(), projectileMount.transform.parent.parent.GetComponent<Collider>());
|
Physics.IgnoreCollision(go.GetComponent<Collider>(), projectileMount.transform.parent.parent.GetComponent<Collider>());
|
||||||
|
|
||||||
// Update the last shot time
|
// Update the last shot time
|
||||||
lastShotTime = NetworkTime.time;
|
runtimeData.lastShotTime = NetworkTime.time;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isClientOnly)
|
if (isClientOnly)
|
||||||
@ -394,54 +476,10 @@ void DoShoot()
|
|||||||
Physics.IgnoreCollision(go.GetComponent<Collider>(), projectileMount.transform.parent.parent.GetComponent<Collider>());
|
Physics.IgnoreCollision(go.GetComponent<Collider>(), projectileMount.transform.parent.parent.GetComponent<Collider>());
|
||||||
|
|
||||||
// Update the last shot time
|
// Update the last shot time
|
||||||
lastShotTime = NetworkTime.time;
|
runtimeData.lastShotTime = NetworkTime.time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Start & Stop Callbacks
|
|
||||||
|
|
||||||
public override void OnStartServer() { }
|
|
||||||
|
|
||||||
public override void OnStartLocalPlayer()
|
|
||||||
{
|
|
||||||
if (turretUIPrefab != null)
|
|
||||||
turretUI = Instantiate(turretUIPrefab);
|
|
||||||
|
|
||||||
if (turretUI != null)
|
|
||||||
{
|
|
||||||
if (turretUI.TryGetComponent(out TurretUI canvasControlPanel))
|
|
||||||
canvasControlPanel.Refresh(moveKeys, optionsKeys);
|
|
||||||
|
|
||||||
turretUI.SetActive(controlOptions.HasFlag(ControlOptions.ShowUI));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnStopLocalPlayer()
|
|
||||||
{
|
|
||||||
if (turretUI != null)
|
|
||||||
Destroy(turretUI);
|
|
||||||
turretUI = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnStartAuthority()
|
|
||||||
{
|
|
||||||
// Calculate DPI-aware sensitivity
|
|
||||||
float dpiScale = (Screen.dpi > 0) ? (Screen.dpi / BASE_DPI) : 1f;
|
|
||||||
mouseSensitivity = turretAcceleration * dpiScale;
|
|
||||||
//Debug.Log($"Screen DPI: {Screen.dpi}, DPI Scale: {dpiScale}, Adjusted Turn Acceleration: {turnAccelerationDPI}");
|
|
||||||
|
|
||||||
SetCursor(controlOptions.HasFlag(ControlOptions.MouseLock));
|
|
||||||
this.enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnStopAuthority()
|
|
||||||
{
|
|
||||||
SetCursor(false);
|
|
||||||
this.enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,8 @@ namespace Mirror.Examples.Common.Controllers.Tank
|
|||||||
public class TankTurretReliable : TankTurretBase
|
public class TankTurretReliable : TankTurretBase
|
||||||
{
|
{
|
||||||
[Header("Components")]
|
[Header("Components")]
|
||||||
public NetworkTransformReliable turretNTR;
|
public NetworkTransformReliable turretNetworkTransform;
|
||||||
public NetworkTransformReliable barrelNTR;
|
public NetworkTransformReliable barrelNetworkTransform;
|
||||||
|
|
||||||
protected override void Reset()
|
protected override void Reset()
|
||||||
{
|
{
|
||||||
@ -22,41 +22,39 @@ protected override void Reset()
|
|||||||
|
|
||||||
if (NTs.Length < 2)
|
if (NTs.Length < 2)
|
||||||
{
|
{
|
||||||
turretNTR = gameObject.AddComponent<NetworkTransformReliable>();
|
turretNetworkTransform = gameObject.AddComponent<NetworkTransformReliable>();
|
||||||
turretNTR.transform.SetSiblingIndex(NTs[0].transform.GetSiblingIndex() + 1);
|
turretNetworkTransform.transform.SetSiblingIndex(NTs[0].transform.GetSiblingIndex() + 1);
|
||||||
NTs = GetComponents<NetworkTransformReliable>();
|
NTs = GetComponents<NetworkTransformReliable>();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
turretNTR = NTs[1];
|
turretNetworkTransform = NTs[1];
|
||||||
|
|
||||||
// Ensure SyncDirection is Client to Server
|
// Ensure syncDirection is Client to Server
|
||||||
turretNTR.syncDirection = SyncDirection.ClientToServer;
|
turretNetworkTransform.syncDirection = SyncDirection.ClientToServer;
|
||||||
turretNTR.syncPosition = false;
|
|
||||||
|
|
||||||
// Set SyncPosition to false because we only want to sync rotation
|
// Set syncPosition to false because we only want to sync rotation
|
||||||
//turretNTR.syncPosition = false;
|
turretNetworkTransform.syncPosition = false;
|
||||||
|
|
||||||
if (base.turret != null)
|
if (base.turret != null)
|
||||||
turretNTR.target = turret;
|
turretNetworkTransform.target = turret;
|
||||||
|
|
||||||
if (NTs.Length < 3)
|
if (NTs.Length < 3)
|
||||||
{
|
{
|
||||||
barrelNTR = gameObject.AddComponent<NetworkTransformReliable>();
|
barrelNetworkTransform = gameObject.AddComponent<NetworkTransformReliable>();
|
||||||
barrelNTR.transform.SetSiblingIndex(NTs[1].transform.GetSiblingIndex() + 1);
|
barrelNetworkTransform.transform.SetSiblingIndex(NTs[1].transform.GetSiblingIndex() + 1);
|
||||||
NTs = GetComponents<NetworkTransformReliable>();
|
NTs = GetComponents<NetworkTransformReliable>();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
barrelNTR = NTs[2];
|
barrelNetworkTransform = NTs[2];
|
||||||
|
|
||||||
// Ensure SyncDirection is Client to Server
|
// Ensure syncDirection is Client to Server
|
||||||
barrelNTR.syncDirection = SyncDirection.ClientToServer;
|
barrelNetworkTransform.syncDirection = SyncDirection.ClientToServer;
|
||||||
barrelNTR.syncPosition = false;
|
|
||||||
|
|
||||||
// Set SyncPosition to false because we only want to sync rotation
|
// Set syncPosition to false because we only want to sync rotation
|
||||||
//barrelNTR.syncPosition = false;
|
barrelNetworkTransform.syncPosition = false;
|
||||||
|
|
||||||
if (barrel != null)
|
if (barrel != null)
|
||||||
barrelNTR.target = barrel;
|
barrelNetworkTransform.target = barrel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,8 @@ namespace Mirror.Examples.Common.Controllers.Tank
|
|||||||
public class TankTurretUnreliable : TankTurretBase
|
public class TankTurretUnreliable : TankTurretBase
|
||||||
{
|
{
|
||||||
[Header("Components")]
|
[Header("Components")]
|
||||||
public NetworkTransformUnreliable turretNTR;
|
public NetworkTransformUnreliable turretNetworkTransform;
|
||||||
public NetworkTransformUnreliable barrelNTR;
|
public NetworkTransformUnreliable barrelNetworkTransform;
|
||||||
|
|
||||||
protected override void Reset()
|
protected override void Reset()
|
||||||
{
|
{
|
||||||
@ -22,41 +22,39 @@ protected override void Reset()
|
|||||||
|
|
||||||
if (NTs.Length < 2)
|
if (NTs.Length < 2)
|
||||||
{
|
{
|
||||||
turretNTR = gameObject.AddComponent<NetworkTransformUnreliable>();
|
turretNetworkTransform = gameObject.AddComponent<NetworkTransformUnreliable>();
|
||||||
turretNTR.transform.SetSiblingIndex(NTs[0].transform.GetSiblingIndex() + 1);
|
turretNetworkTransform.transform.SetSiblingIndex(NTs[0].transform.GetSiblingIndex() + 1);
|
||||||
NTs = GetComponents<NetworkTransformUnreliable>();
|
NTs = GetComponents<NetworkTransformUnreliable>();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
turretNTR = NTs[1];
|
turretNetworkTransform = NTs[1];
|
||||||
|
|
||||||
// Ensure SyncDirection is Client to Server
|
// Ensure syncDirection is Client to Server
|
||||||
turretNTR.syncDirection = SyncDirection.ClientToServer;
|
turretNetworkTransform.syncDirection = SyncDirection.ClientToServer;
|
||||||
turretNTR.syncPosition = false;
|
|
||||||
|
|
||||||
// Set SyncPosition to false because we only want to sync rotation
|
// Set syncPosition to false because we only want to sync rotation
|
||||||
//turretNTR.syncPosition = false;
|
turretNetworkTransform.syncPosition = false;
|
||||||
|
|
||||||
if (base.turret != null)
|
if (base.turret != null)
|
||||||
turretNTR.target = turret;
|
turretNetworkTransform.target = turret;
|
||||||
|
|
||||||
if (NTs.Length < 3)
|
if (NTs.Length < 3)
|
||||||
{
|
{
|
||||||
barrelNTR = gameObject.AddComponent<NetworkTransformUnreliable>();
|
barrelNetworkTransform = gameObject.AddComponent<NetworkTransformUnreliable>();
|
||||||
barrelNTR.transform.SetSiblingIndex(NTs[1].transform.GetSiblingIndex() + 1);
|
barrelNetworkTransform.transform.SetSiblingIndex(NTs[1].transform.GetSiblingIndex() + 1);
|
||||||
NTs = GetComponents<NetworkTransformUnreliable>();
|
NTs = GetComponents<NetworkTransformUnreliable>();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
barrelNTR = NTs[2];
|
barrelNetworkTransform = NTs[2];
|
||||||
|
|
||||||
// Ensure SyncDirection is Client to Server
|
// Ensure syncDirection is Client to Server
|
||||||
barrelNTR.syncDirection = SyncDirection.ClientToServer;
|
barrelNetworkTransform.syncDirection = SyncDirection.ClientToServer;
|
||||||
barrelNTR.syncPosition = false;
|
|
||||||
|
|
||||||
// Set SyncPosition to false because we only want to sync rotation
|
// Set syncPosition to false because we only want to sync rotation
|
||||||
//barrelNTR.syncPosition = false;
|
barrelNetworkTransform.syncPosition = false;
|
||||||
|
|
||||||
if (barrel != null)
|
if (barrel != null)
|
||||||
barrelNTR.target = barrel;
|
barrelNetworkTransform.target = barrel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user