{
}
目前效果:
鼠标旋转视角,wasd控制移动
重写了UnityChan的控制代码,因为自带的UnityChanControlScriptWithRgidBody的操作很蹩脚,AD键不是向着左右两边走,而是旋转 ==|| 这。。
可能各有所好?我最喜欢的巫师3:狂猎和塞尔达传说:荒野之息都是按W向着屏幕的正前方走,AD向着屏幕左边缘和有边缘方向走,通过旋转鼠标来确定方向,所以我也打算这么做。
首先,写了一个相机跟随+转向脚本,思路很简单,
根据鼠标滑动方向旋转摄像机,根据一个设置好的距离调整摄像机到Chan的距离,
可以设置X轴的最大最小旋转角度,
因为总是卡到地下或者墙里,所以用射线检测来检测摄像机是否能继续旋转,比如:摄像机的高度已经很接近地面了,我们就不能让它在向下移动了。
相机跟随+转向脚本如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Camerafollow : MonoBehaviour {
public Transform Chan;
[Range(0,180)]
public float max_X_Euler;
[Range(-180, 0)]
public float min_X_Euler;
[Range(0, 60)]
public float X_rotateSpeed;
[Range(0, 60)]
public float Y_rotateSpeed;
[Range(1,10)]
public float XZ_dist;
[Range(1, 10)]
public float Y_dist;
private float current_rotation_X;
private float current_rotation_Y;
private float last_rotation_X;
private float last_rotation_Y;
private bool[] Xobstacle;
private bool[] Yobstacle;
// Use this for initialization
void Start () {
Xobstacle = new bool[2];
Yobstacle = new bool[2];
}
// Update is called once per frame
void LateUpdate () {
current_rotation_X += Input.GetAxis("Mouse Y") * X_rotateSpeed;
current_rotation_Y+= Input.GetAxis("Mouse X") * Y_rotateSpeed;
Xobstacle[0] = Physics.Linecast(transform.position, transform.position + new Vector3(0, -1.5f, 0));
Xobstacle[1] = Physics.Linecast(transform.position, transform.position + new Vector3(0, -1.5f, 0));
Yobstacle[0] = Physics.Linecast(transform.position, transform.position + transform.TransformDirection(new Vector3(1, 0, 0)));
Yobstacle[1] = Physics.Linecast(transform.position, transform.position + transform.TransformDirection(new Vector3(-1, 0, 0)));
Physics.Linecast(transform.position, transform.position + new Vector3(1, 0, 0));
current_rotation_X = Mathf.Clamp(current_rotation_X, min_X_Euler, max_X_Euler);
if (Xobstacle[0] && current_rotation_X > last_rotation_X)
{
current_rotation_X = last_rotation_X;
}
else if(Xobstacle[1]&& current_rotation_X < last_rotation_X)
{
current_rotation_X = last_rotation_X;
}
if (Yobstacle[0]&¤t_rotation_Y<last_rotation_Y)
{
current_rotation_Y = last_rotation_Y;
}
else if (Yobstacle[1] && current_rotation_Y > last_rotation_Y)
{
current_rotation_Y = last_rotation_Y;
}
transform.localEulerAngles = new Vector3(-current_rotation_X, current_rotation_Y, 0f);
transform.position = Chan.position;
transform.Translate(Vector3.back * XZ_dist, Space.Self);
transform.Translate(Vector3.up * Y_dist, Space.World);
last_rotation_X = current_rotation_X;
last_rotation_Y = current_rotation_Y;
}
}
我模仿了巫师3的人物移动,效果如下:
w:摄像机的前方;a:摄像机的左边方向;aw:摄像机的左前方。以此类推。
首先需要计算出人物需要旋转的角度,因为Chan需要先转身,再朝着那个方向移动,
核心语句:
angle = (Vector3.Dot(Chan.right, direction[1]) > 0 ? 1 : -1)
* Vector2.Angle(new Vector2(direction[0].x, direction[0].z), new Vector2(direction[1].x, direction[1].z));
通过向量点乘判断角度的正负,direction[0]是chan的朝向,direction[1]存放的是chan将要朝向的方向,正方向都好说通过forward和right可以方便得到,类似左前方这样的,我是乘了一个旋转矩阵,比如左前方就是a*[45°的旋转矩阵]得到的:
private Matrix4x4 rotate45 = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0, 45, 0), Vector3.one);
direction[1] = rotate45.MultiplyPoint3x4(-CameraforChan.right);
Chan的全部移动代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movewithrigidbody2 : MonoBehaviour {
[Range(0.1f, 10f)]
public float moveSpeed;
[Range(.1f, 2.66f)]
public float jumpHeight;
[Range(10, 360f)]
public float rotationSpeed;
private Animator An;
private Rigidbody Ri;
private Transform Chan;
public Transform CameraforChan;
private Vector3[] direction;
private float angle;
private bool rotating;
private Matrix4x4 rotate45 = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0, 45, 0), Vector3.one);
private AnimatorStateInfo currentBaseState;
static int jumpState = Animator.StringToHash("Base Layer.Jump");
void Start()
{
An = this.GetComponent<Animator>();
Ri = this.GetComponent<Rigidbody>();
Chan = this.transform;
direction = new Vector3[2];
rotating = false;
}
private void LateUpdate()
{
Movepos(Input.GetAxis("Horizontal"),Input.GetAxis("Vertical"));
Jump(Input.GetAxis("Jump"));
}
private void Jump(float var)
{
An.SetBool("Jump", false);
if (var > 0 && !An.IsInTransition(0))
{
An.SetBool("Jump", true);
Ri.velocity = new Vector3(0, Mathf.Sqrt(2 * 9.8f * jumpHeight), 0);
}
}
private void Movepos(float LR,float FB)
{
float var = FB != 0 ? FB : LR;
currentBaseState = An.GetCurrentAnimatorStateInfo(0);
if (currentBaseState.fullPathHash != jumpState)
{
An.SetFloat("Speed",Mathf.Abs(var));
if (var!=0)
{
Calculation(FB>0&&LR<0?4: FB > 0 && LR > 0 ? 5:FB < 0 && LR < 0 ?6: FB < 0 && LR > 0 ?7
: FB > 0 ? 0 : FB < 0 ? 1 : LR < 0 ? 2 : 3);
if (!rotating)
{
Ri.MovePosition(Chan.position + direction[1] * Time.fixedDeltaTime * moveSpeed);
}
}
}
}
private void Calculation(int LRFB)
{
direction[0] = Chan.forward;
switch (LRFB) {
case 4:
direction[1] = rotate45.MultiplyPoint3x4(-CameraforChan.right);
break;
case 5:
direction[1] = rotate45.MultiplyPoint3x4(CameraforChan.forward);
break;
case 6:
direction[1] = rotate45.MultiplyPoint3x4(-CameraforChan.forward);
break;
case 7:
direction[1] = rotate45.MultiplyPoint3x4(CameraforChan.right);
break;
case 0:
direction[1] = CameraforChan.forward;
break;
case 1:
direction[1] = -CameraforChan.forward;
break;
case 2:
direction[1] = -CameraforChan.right;
break;
case 3:
direction[1] = CameraforChan.right;
break;
}
angle = (Vector3.Dot(Chan.right, direction[1]) > 0 ? 1 : -1)
* Vector2.Angle(new Vector2(direction[0].x, direction[0].z), new Vector2(direction[1].x, direction[1].z));
if (Mathf.Abs(angle) <90)
{
rotating = false;
}
else
{
rotating = true;
}
if (Mathf.Abs(angle) > 10)
{
Ri.MoveRotation(Quaternion.Euler(Chan.rotation.eulerAngles+new Vector3(0, angle * rotationSpeed * Time.fixedDeltaTime, 0)));
}
}
}
========================================================================
和高中同学聊了聊,好像龙腾世纪3是左右旋转的设定,我之前轻改了一下UnityChanControlScriptWithRgidBody,把一些没用的去掉了,也不是说不能用,只是我感觉有点蹩脚。。。代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movewithrigidbody : MonoBehaviour {
[Range(0.1f,10f)]
public float moveSpeedfront;
[Range(0.1f, 10f)]
public float moveSpeedback;
[Range(.1f, 2.66f)]
public float jumpHeight;
[Range(10, 360f)]
public float rotationSpeed;
private Animator An;
private Rigidbody Ri;
//private Collider Co;
private Transform Chan;
private AnimatorStateInfo currentBaseState;
static int jumpState = Animator.StringToHash("Base Layer.Jump");
void Start () {
An = this.GetComponent<Animator>();
Ri = this.GetComponent<Rigidbody>();
Chan = this.transform;
}
private void Update()
{
if(Input.GetMouseButtonDown(0))Cursor.visible = false;
}
void FixedUpdate () {
currentBaseState = An.GetCurrentAnimatorStateInfo(0);//得到动画状态机Layers0的状态信息
if (currentBaseState.fullPathHash != jumpState)
{
if (!An.IsInTransition(0))
{
An.SetBool("Jump", false);
}
Frontbackmove(Input.GetAxis("Vertical"));
Jump(Input.GetButtonDown("Jump"));
Rotatechange(Input.GetAxis("Horizontal"));
}
else
{
float ver = Input.GetAxis("Vertical");
if (ver > 0)
{
Ri.AddForce(Chan.TransformDirection(new Vector3(0, 0, ver * moveSpeedfront * Time.fixedDeltaTime*36)),ForceMode.VelocityChange);
}
}
}
private void Frontbackmove(float ver)
{
if (Input.GetAxis("LSHIFT") > 0) ver *= .36f;
An.SetFloat("Speed", ver);
if (ver > 0.1f)
{
Ri.MovePosition(Chan.position+Chan.TransformDirection(new Vector3(0, 0, ver * moveSpeedfront*Time.fixedDeltaTime)));
}
else if(ver < -0.1f)
{
Ri.MovePosition(Chan.position+Chan.TransformDirection(new Vector3(0, 0, ver * moveSpeedback * Time.fixedDeltaTime)));
}
}
private void Jump(bool ver)
{
if (ver)
{
An.SetBool("Jump", true);
Ri.velocity = new Vector3(0, Mathf.Sqrt(2 * 9.8f * jumpHeight),0);
}
}
public void Rotatechange(float ver)
{
An.SetFloat("Direction", ver);
if (ver != 0)
{
Ri.MoveRotation(Quaternion.Euler(Chan.rotation.eulerAngles+new Vector3(0,ver*rotationSpeed*Time.fixedDeltaTime,0)));
}
}
}
另外,帧数比较低的原因是unity自带terrain的草没有批合,打算用Mesh地形+gpuinstance来重做地形,感觉可控性高一点。