您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

266 行
8.3 KiB

using UnityEngine;
using UnityEngine.Experimental.U2D.Animation;
using UnityEngine.InputSystem;
public enum GroundType
{
None,
Soft,
Hard
}
public class CharacterController2D : MonoBehaviour
{
readonly Vector3 flippedScale = new Vector3(-1, 1, 1);
readonly Quaternion flippedRotation = new Quaternion(0, 0, 1, 0);
[Header("Character")]
[SerializeField] Animator animator = null;
[SerializeField] Transform puppet = null;
[SerializeField] CharacterAudio audioPlayer = null;
[Header("Tail")]
[SerializeField] Transform tailAnchor = null;
[SerializeField] Rigidbody2D tailRigidbody = null;
[Header("Equipment")]
[SerializeField] Transform handAnchor = null;
[SerializeField] SpriteLibrary spriteLibrary = null;
[Header("Movement")]
[SerializeField] float acceleration = 0.0f;
[SerializeField] float maxSpeed = 0.0f;
[SerializeField] float jumpForce = 0.0f;
[SerializeField] float minFlipSpeed = 0.1f;
[SerializeField] float jumpGravityScale = 1.0f;
[SerializeField] float fallGravityScale = 1.0f;
[SerializeField] float groundedGravityScale = 1.0f;
[SerializeField] bool resetSpeedOnLand = false;
private Rigidbody2D controllerRigidbody;
private Collider2D controllerCollider;
private LayerMask softGroundMask;
private LayerMask hardGroundMask;
private Vector2 movementInput;
private bool jumpInput;
private Vector2 prevVelocity;
private GroundType groundType;
private bool isFlipped;
private bool isJumping;
private bool isFalling;
private int animatorGroundedBool;
private int animatorRunningSpeed;
private int animatorJumpTrigger;
public bool CanMove { get; set; }
void Start()
{
#if UNITY_EDITOR
if (Keyboard.current == null)
{
var playerSettings = new UnityEditor.SerializedObject(Resources.FindObjectsOfTypeAll<UnityEditor.PlayerSettings>()[0]);
var newInputSystemProperty = playerSettings.FindProperty("enableNativePlatformBackendsForNewInputSystem");
bool newInputSystemEnabled = newInputSystemProperty != null ? newInputSystemProperty.boolValue : false;
if (newInputSystemEnabled)
{
var msg = "New Input System backend is enabled but it requires you to restart Unity, otherwise the player controls won't work. Do you want to restart now?";
if (UnityEditor.EditorUtility.DisplayDialog("Warning", msg, "Yes", "No"))
{
UnityEditor.EditorApplication.ExitPlaymode();
var dataPath = Application.dataPath;
var projectPath = dataPath.Substring(0, dataPath.Length - 7);
UnityEditor.EditorApplication.OpenProject(projectPath);
}
}
}
#endif
controllerRigidbody = GetComponent<Rigidbody2D>();
controllerCollider = GetComponent<Collider2D>();
softGroundMask = LayerMask.GetMask("Ground Soft");
hardGroundMask = LayerMask.GetMask("Ground Hard");
animatorGroundedBool = Animator.StringToHash("Grounded");
animatorRunningSpeed = Animator.StringToHash("RunningSpeed");
animatorJumpTrigger = Animator.StringToHash("Jump");
CanMove = true;
}
void Update()
{
var keyboard = Keyboard.current;
if (!CanMove || keyboard == null)
return;
// Horizontal movement
float moveHorizontal = 0.0f;
if (keyboard.leftArrowKey.isPressed || keyboard.aKey.isPressed)
moveHorizontal = -1.0f;
else if (keyboard.rightArrowKey.isPressed || keyboard.dKey.isPressed)
moveHorizontal = 1.0f;
movementInput = new Vector2(moveHorizontal, 0);
// Jumping input
if (!isJumping && keyboard.spaceKey.wasPressedThisFrame)
jumpInput = true;
}
void FixedUpdate()
{
UpdateGrounding();
UpdateVelocity();
UpdateDirection();
UpdateJump();
UpdateTailPose();
UpdateGravityScale();
prevVelocity = controllerRigidbody.velocity;
}
private void UpdateGrounding()
{
// Use character collider to check if touching ground layers
if (controllerCollider.IsTouchingLayers(softGroundMask))
groundType = GroundType.Soft;
else if (controllerCollider.IsTouchingLayers(hardGroundMask))
groundType = GroundType.Hard;
else
groundType = GroundType.None;
// Update animator
animator.SetBool(animatorGroundedBool, groundType != GroundType.None);
}
private void UpdateVelocity()
{
Vector2 velocity = controllerRigidbody.velocity;
// Apply acceleration directly as we'll want to clamp
// prior to assigning back to the body.
velocity += movementInput * acceleration * Time.fixedDeltaTime;
// We've consumed the movement, reset it.
movementInput = Vector2.zero;
// Clamp horizontal speed.
velocity.x = Mathf.Clamp(velocity.x, -maxSpeed, maxSpeed);
// Assign back to the body.
controllerRigidbody.velocity = velocity;
// Update animator running speed
var horizontalSpeedNormalized = Mathf.Abs(velocity.x) / maxSpeed;
animator.SetFloat(animatorRunningSpeed, horizontalSpeedNormalized);
// Play audio
audioPlayer.PlaySteps(groundType, horizontalSpeedNormalized);
}
private void UpdateJump()
{
// Set falling flag
if (isJumping && controllerRigidbody.velocity.y < 0)
isFalling = true;
// Jump
if (jumpInput && groundType != GroundType.None)
{
// Jump using impulse force
controllerRigidbody.AddForce(new Vector2(0, jumpForce), ForceMode2D.Impulse);
// Set animator
animator.SetTrigger(animatorJumpTrigger);
// We've consumed the jump, reset it.
jumpInput = false;
// Set jumping flag
isJumping = true;
// Play audio
audioPlayer.PlayJump();
}
// Landed
else if (isJumping && isFalling && groundType != GroundType.None)
{
// Since collision with ground stops rigidbody, reset velocity
if (resetSpeedOnLand)
{
prevVelocity.y = controllerRigidbody.velocity.y;
controllerRigidbody.velocity = prevVelocity;
}
// Reset jumping flags
isJumping = false;
isFalling = false;
// Play audio
audioPlayer.PlayLanding(groundType);
}
}
private void UpdateDirection()
{
// Use scale to flip character depending on direction
if (controllerRigidbody.velocity.x > minFlipSpeed && isFlipped)
{
isFlipped = false;
puppet.localScale = Vector3.one;
}
else if (controllerRigidbody.velocity.x < -minFlipSpeed && !isFlipped)
{
isFlipped = true;
puppet.localScale = flippedScale;
}
}
private void UpdateTailPose()
{
// Calculate the extrapolated target position of the tail anchor.
Vector2 targetPosition = tailAnchor.position;
targetPosition += controllerRigidbody.velocity * Time.fixedDeltaTime;
tailRigidbody.MovePosition(targetPosition);
if (isFlipped)
tailRigidbody.SetRotation(tailAnchor.rotation * flippedRotation);
else
tailRigidbody.SetRotation(tailAnchor.rotation);
}
private void UpdateGravityScale()
{
// Use grounded gravity scale by default.
var gravityScale = groundedGravityScale;
if (groundType == GroundType.None)
{
// If not grounded then set the gravity scale according to upwards (jump) or downwards (falling) motion.
gravityScale = controllerRigidbody.velocity.y > 0.0f ? jumpGravityScale : fallGravityScale;
}
controllerRigidbody.gravityScale = gravityScale;
}
public void GrabItem(Transform item)
{
// Attach item to hand
item.SetParent(handAnchor, false);
item.localPosition = Vector3.zero;
item.localRotation = Quaternion.identity;
}
public void SwapSprites(SpriteLibraryAsset spriteLibraryAsset)
{
spriteLibrary.spriteLibraryAsset = spriteLibraryAsset;
}
}