本篇文章主要列举了第三人称的多种控制方式。

超级玛丽单机版操作方法和技巧-单机超级玛丽最新版本

一、官方实例的第三人称控制方式。

该控制方式比较复杂,但是却写得很好很完善,并且运用了新的动画系统。大家可以下载官方的角色控制包来使用,附上图一张,不多说。

二、老版官方的第三人称控制方式。

大家应该知道老版的第三人称控制方式是用JavaScript脚本写的,可能大家拿过来还不太好用,但是这里我们把它改写成C#脚本(PS:参照雨松的修改),这样用起来就方便多了,而且用的是经典版的动画系统,满足了很多人的需求。

在unity中,新版的mecanim动画系统出现,虽然说很实用,在某些方面解决了很多人的需求,但这并不意味着可以替代原版经典的动画系统,所以到现在为止,两种动画都是通用的。

using UnityEngine;

using System.Collections;

[RequireComponent(typeof(CharacterController))]

public class ThirdPersonController111 : MonoBehaviour

{

public AnimationClip idleAnimation;

public AnimationClip walkAnimation;

public AnimationClip runAnimation;

public AnimationClip jumpPoseAnimation;

public float walkMaxAnimationSpeed = 0.75f;

public float trotMaxAnimationSpeed = 1.0f;

public float runMaxAnimationSpeed = 1.0f;

public float jumpAnimationSpeed = 1.15f;

public float landAnimationSpeed = 1.0f;

private Animation _animation;

enum CharacterState

{

Idle = 0,

Walking = 1,

Trotting = 2,

Running = 3,

Jumping = 4,

}

private CharacterState _characterState;

// The speed when walking

float walkSpeed = 2.0f;

// after trotAfterSeconds of walking we trot with trotSpeed

float trotSpeed = 4.0f;

// when pressing "Fire3" button (cmd) we start running

float runSpeed = 6.0f;

float inAirControlAcceleration = 3.0f;

// How high do we jump when pressing jump and letting go immediately

float jumpHeight = 0.5f;

// The gravity for the character

float gravity = 20.0f;

// The gravity in controlled descent mode

float speedSmoothing = 10.0f;

float rotateSpeed = 500.0f;

float trotAfterSeconds = 3.0f;

bool canJump = true;

private float jumpRepeatTime = 0.05f;

private float jumpTimeout = 0.15f;

private float groundedTimeout = 0.25f;

// The camera doesnt start following the target immediately but waits for a split second to avoid too much waving around.

private float lockCameraTimer = 0.0f;

// The current move direction in x-z

private Vector3 moveDirection = Vector3.zero;

// The current vertical speed

private float verticalSpeed = 0.0f;

// The current x-z move speed

private float moveSpeed = 0.0f;

// The last collision flags returned from controller.Move

private CollisionFlags collisionFlags;

// Are we jumping? (Initiated with jump button and not grounded yet)

private bool jumping = false;

private bool jumpingReachedApex = false;

// Are we moving backwards (This locks the camera to not do a 180 degree spin)

private bool movingBack = false;

// Is the user pressing any keys?

private bool isMoving = false;

// When did the user start walking (Used for going into trot after a while)

private float walkTimeStart = 0.0f;

// Last time the jump button was clicked down

private float lastJumpButtonTime = -10.0f;

// Last time we performed a jump

private float lastJumpTime = -1.0f;

// the height we jumped from (Used to determine for how long to apply extra jump power after jumping.)

private float lastJumpStartHeight = 0.0f;

private Vector3 inAirVelocity = Vector3.zero;

private float lastGroundedTime = 0.0f;

private bool isControllable = true;

void Awake()

{

moveDirection = transform.TransformDirection(Vector3.forward);

_animation = GetComponent<Animation>();

if (!_animation)

Debug.Log("The character you would like to control doesn't have animations. Moving her might look weird.");

/*

public var idleAnimation : AnimationClip;

public var walkAnimation : AnimationClip;

public var runAnimation : AnimationClip;

public var jumpPoseAnimation : AnimationClip;

*/

if (!idleAnimation)

{

_animation = null;

Debug.Log("No idle animation found. Turning off animations.");

}

if (!walkAnimation)

{

_animation = null;

Debug.Log("No walk animation found. Turning off animations.");

}

if (!runAnimation)

{

_animation = null;

Debug.Log("No run animation found. Turning off animations.");

}

if (!jumpPoseAnimation && canJump)

{

_animation = null;

Debug.Log("No jump animation found and the character has canJump enabled. Turning off animations.");

}

}

void UpdateSmoothedMovementDirection()

{

Transform cameraTransform = Camera.main.transform;

bool grounded = IsGrounded();

// Forward vector relative to the camera along the x-z plane

Vector3 forward = cameraTransform.TransformDirection(Vector3.forward);

forward.y = 0;

forward = forward.normalized;

// Right vector relative to the camera

// Always orthogonal to the forward vector

Vector3 right = new Vector3(forward.z, 0, -forward.x);

float v = Input.GetAxisRaw("Vertical");

float h = Input.GetAxisRaw("Horizontal");

// Are we moving backwards or looking backwards

if (v < -0.2f)

movingBack = true;

else

movingBack = false;

bool wasMoving = isMoving;

isMoving = Mathf.Abs(h) > 0.1f || Mathf.Abs(v) > 0.1f;

// Target direction relative to the camera

Vector3 targetDirection = h * right + v * forward;

// Grounded controls

if (grounded)

{

// Lock camera for short period when transitioning moving & standing still

lockCameraTimer += Time.deltaTime;

if (isMoving != wasMoving)

lockCameraTimer = 0.0f;

// We store speed and direction seperately,

// so that when the character stands still we still have a valid forward direction

// moveDirection is always normalized, and we only update it if there is user input.

if (targetDirection != Vector3.zero)

{

// If we are really slow, just snap to the target direction

if (moveSpeed < walkSpeed * 0.9f && grounded)

{

moveDirection = targetDirection.normalized;

}

// Otherwise smoothly turn towards it

else

{

moveDirection = Vector3.RotateTowards(moveDirection, targetDirection, rotateSpeed * Mathf.Deg2Rad * Time.deltaTime, 1000);

moveDirection = moveDirection.normalized;

}

}

// Smooth the speed based on the current target direction

float curSmooth = speedSmoothing * Time.deltaTime;

// Choose target speed

//* We want to support analog input but make sure you cant walk faster diagonally than just forward or sideways

float targetSpeed = Mathf.Min(targetDirection.magnitude, 1.0f);

_characterState = CharacterState.Idle;

// Pick speed modifier

if (Input.GetKey(KeyCode.LeftShift) | Input.GetKey(KeyCode.RightShift))

{

targetSpeed *= runSpeed;

_characterState = CharacterState.Running;

}

else if (Time.time - trotAfterSeconds > walkTimeStart)

{

targetSpeed *= trotSpeed;

_characterState = CharacterState.Trotting;

}

else

{

targetSpeed *= walkSpeed;

_characterState = CharacterState.Walking;

}

moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, curSmooth);

// Reset walk time start when we slow down

if (moveSpeed < walkSpeed * 0.3f)

walkTimeStart = Time.time;

}

// In air controls

else

{

// Lock camera while in air

if (jumping)

lockCameraTimer = 0.0f;

if (isMoving)

inAirVelocity += targetDirection.normalized * Time.deltaTime * inAirControlAcceleration;

}

}

void ApplyJumping()

{

// Prevent jumping too fast after each other

if (lastJumpTime + jumpRepeatTime > Time.time)

return;

if (IsGrounded())

{

// Jump

// - Only when pressing the button down

// - With a timeout so you can press the button slightly before landing

if (canJump && Time.time < lastJumpButtonTime + jumpTimeout)

{

verticalSpeed = CalculateJumpVerticalSpeed(jumpHeight);

SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);

}

}

}

void ApplyGravity()

{

if (isControllable) // don't move player at all if not controllable.

{

// Apply gravity

bool jumpButton = Input.GetButton("Jump");

// When we reach the apex of the jump we send out a message

if (jumping && !jumpingReachedApex && verticalSpeed <= 0.0f)

{

jumpingReachedApex = true;

SendMessage("DidJumpReachApex", SendMessageOptions.DontRequireReceiver);

}

if (IsGrounded())

verticalSpeed = 0.0f;

else

verticalSpeed -= gravity * Time.deltaTime;

}

}

float CalculateJumpVerticalSpeed(float targetJumpHeight)

{

// From the jump height and gravity we deduce the upwards speed

// for the character to reach at the apex.

return Mathf.Sqrt(2 * targetJumpHeight * gravity);

}

void DidJump()

{

jumping = true;

jumpingReachedApex = false;

lastJumpTime = Time.time;

lastJumpStartHeight = transform.position.y;

lastJumpButtonTime = -10;

_characterState = CharacterState.Jumping;

}

void Update()

{

if (!isControllable)

{

// kill all inputs if not controllable.

Input.ResetInputAxes();

}

if (Input.GetButtonDown("Jump"))

{

lastJumpButtonTime = Time.time;

}

UpdateSmoothedMovementDirection();

// Apply gravity

// - extra power jump modifies gravity

// - controlledDescent mode modifies gravity

ApplyGravity();

// Apply jumping logic

ApplyJumping();

// Calculate actual motion

Vector3 movement = moveDirection * moveSpeed + new Vector3(0, verticalSpeed, 0) + inAirVelocity;

movement *= Time.deltaTime;

// Move the controller

CharacterController controller = GetComponent<CharacterController>();

collisionFlags = controller.Move(movement);

// ANIMATION sector

if (_animation)

{

if (_characterState == CharacterState.Jumping)

{

if (!jumpingReachedApex)

{

_animation[jumpPoseAnimation.name].speed = jumpAnimationSpeed;

_animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever;

_animation.CrossFade(jumpPoseAnimation.name);

}

else

{

_animation[jumpPoseAnimation.name].speed = -landAnimationSpeed;

_animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever;

_animation.CrossFade(jumpPoseAnimation.name);

}

}

else

{

if (controller.velocity.sqrMagnitude < 0.1f)

{

_animation.CrossFade(idleAnimation.name);

}

else

{

if (_characterState == CharacterState.Running)

{

_animation[runAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0.0f, runMaxAnimationSpeed);

_animation.CrossFade(runAnimation.name);

}

else if (_characterState == CharacterState.Trotting)

{

_animation[walkAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0.0f, trotMaxAnimationSpeed);

_animation.CrossFade(walkAnimation.name);

}

else if (_characterState == CharacterState.Walking)

{

_animation[walkAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0.0f, walkMaxAnimationSpeed);

_animation.CrossFade(walkAnimation.name);

}

}

}

}

// ANIMATION sector

// Set rotation to the move direction

if (IsGrounded())

{

transform.rotation = Quaternion.LookRotation(moveDirection);

}

else

{

Vector3 xzMove = movement;

xzMove.y = 0;

if (xzMove.sqrMagnitude > 0.001f)

{

transform.rotation = Quaternion.LookRotation(xzMove);

}

}

// We are in jump mode but just became grounded

if (IsGrounded())

{

lastGroundedTime = Time.time;

inAirVelocity = Vector3.zero;

if (jumping)

{

jumping = false;

SendMessage("DidLand", SendMessageOptions.DontRequireReceiver);

}

}

}

void OnControllerColliderHit(ControllerColliderHit hit)

{

// Debug.DrawRay(hit.point, hit.normal);

if (hit.moveDirection.y > 0.01f)

return;

}

float GetSpeed()

{

return moveSpeed;

}

public bool IsJumping()

{

return jumping;

}

bool IsGrounded()

{

return (collisionFlags & CollisionFlags.CollidedBelow) != 0;

}

Vector3 GetDirection()

{

return moveDirection;

}

public bool IsMovingBackwards()

{

return movingBack;

}

public float GetLockCameraTimer()

{

return lockCameraTimer;

}

bool IsMoving()

{

return Mathf.Abs(Input.GetAxisRaw("Vertical")) + Mathf.Abs(Input.GetAxisRaw("Horizontal")) > 0.5f;

}

bool HasJumpReachedApex()

{

return jumpingReachedApex;

}

bool IsGroundedWithTimeout()

{

return lastGroundedTime + groundedTimeout > Time.time;

}

void Reset()

{

gameObject.tag = "Player";

}

}

图一张:

三、根据需求,自己写自己需要的控制方式。

在本期训练营中,主角超级玛丽我才用了一种比较简洁的控制方式,因为这种方式已经能够满足需求,该种方式就是前后左右移动的方式。该方式不需要添加charactercontroller,只需添加胶囊体就可。(PS:不过该方式有个缺点就是必须朝向固定,也就是只能朝向Z轴正方向)

代码如下:

using UnityEngine;

using System.Collections;

public class MarioMove : MonoBehaviour

{

public float speed = 5.0f;

public static bool isGround;

public static bool IsAllowJump;

[SerializeField]

float m_StationaryTurnSpeed = 180;

[SerializeField]

float m_MovingTurnSpeed = 360;

float m_ForwardAmount;

float m_TurnAmount;

Vector3 m_GroundNormal;

private Vector3 m_Move;

private Transform m_Cam;

private Vector3 m_CamForward;

// Use this for initialization

void Start()

{

// get the transform of the main camera

if (Camera.main != null)

{

m_Cam = Camera.main.transform;

}

else

{

Debug.LogWarning(

"Warning: no main camera found. Third person character needs a Camera tagged \"MainCamera\", for camera-relative controls.");

// we use self-relative controls in this case, which probably isn't what the user wants, but hey, we warned them!

}

}

void OnCollisionEnter(Collision collision)

{

//if (collision.collider.tag == "Ground")

if (collision.collider.tag != null)

{

isGround = true;

IsAllowJump = true;

}

else

{

isGround = false;

}

}

// Update is called once per frame

void Update()

{

float h = Input.GetAxis("Horizontal");

float v = Input.GetAxis("Vertical");

GetComponent<Rigidbody>().MovePosition(transform.position - new Vector3(h, 0, v) * speed * Time.deltaTime);

if (isGround == true && Input.GetButton("Jump"))

{

if (IsAllowJump == true)

{

transform.GetComponentInChildren<Animation>().CrossFade("jump");

GetComponent<Rigidbody>().MovePosition(transform.position - new Vector3(-h * 0.1f, -0.15f, -v * 0.1f));

}

}

else if (Input.GetButtonUp("Jump"))

{

IsAllowJump = false;

}

else

{

if (Input.GetAxis("Vertical") > 0.5f ||

Input.GetAxis("Vertical") < -0.5f ||

Input.GetAxis("Horizontal") > 0.5f ||

Input.GetAxis("Horizontal") < -0.5f)

{

transform.GetComponentInChildren<Animation>().CrossFade("run");

}

else if ((Input.GetAxis("Vertical") > 0.0f && Input.GetAxis("Vertical") < 0.5f) ||

(Input.GetAxis("Vertical") > -0.5f && Input.GetAxis("Vertical") < 0.0f) ||

(Input.GetAxis("Horizontal") > 0.0f && Input.GetAxis("Horizontal") < 0.5f) ||

(Input.GetAxis("Horizontal") < 0.0f && Input.GetAxis("Horizontal") > -0.5f))

{

transform.GetComponentInChildren<Animation>().CrossFade("walk");

}

else

{

transform.GetComponentInChildren<Animation>().CrossFade("idle");

}

}

if (m_Cam != null)

{

// calculate camera relative direction to move:

m_CamForward = Vector3.Scale(m_Cam.forward, new Vector3(1, 0, 1)).normalized;

m_Move = v * m_CamForward + h * m_Cam.right;

}

else

{

// we use world-relative directions in the case of no main camera

m_Move = v * Vector3.forward + h * Vector3.right;

}

Move(m_Move);

}

public void Move(Vector3 move)

{

// convert the world relative moveInput vector into a local-relative

// turn amount and forward amount required to head in the desired

// direction.

if (move.magnitude > 1f) move.Normalize();

move = transform.InverseTransformDirection(move);

//CheckGroundStatus();

move = Vector3.ProjectOnPlane(move, m_GroundNormal);

m_TurnAmount = Mathf.Atan2(move.x, move.z);

m_ForwardAmount = move.z;

ApplyExtraTurnRotation();

}

void ApplyExtraTurnRotation()

{

// help the character turn faster (this is in addition to root rotation in the animation)

float turnSpeed = Mathf.Lerp(m_StationaryTurnSpeed, m_MovingTurnSpeed, m_ForwardAmount);

transform.Rotate(0, m_TurnAmount * turnSpeed * Time.deltaTime, 0);

}

}

在跳跃的代码部分,这样写的目的是实现了按跳跃键的时间长短跳的高度不同,和大家小时候玩的超级玛丽游戏的感觉很像。

附图一张:

标签: