最近在项目中遇到一个需求,要求软件中人物的操作为第一人称视角,并且不包含重力。要求用户可以自定义重力。
在经过一番思考之后,我想到了一种解决方案,那就是通过控制相机物体的transform属性,来对相机进行移动。至于相机的视角,我参考了unity自带的第一人称视角控制器的代码来实现。经过一番折腾是实现了,但是在测试过程中发现了一个问题。
那就是
在移动过程中,相机穿越了墙壁!!!!
就算给相机物体添加一个立方体gameobject作为父物体,并且给该父物体添加刚体,也还是没有用。墙壁的碰撞器虽然会触发碰撞事件。但是并没有作用,相机还是会穿越墙壁。这样完全不符合实际的使用需求。经过一番思考,猜测,验证。并参考了系统的FirstPersonController文件,终于找到了原因。原因是对于相机的移动,我是直接了对物体的transform值进行更改来使物体移动,这样我虽然给物体添加了刚体,但是物体并有进行物理学运动,因此物体不会遵循物理学运动现象。
知道了原因,那么就好结局了,既然是因为我对物体进行移动的操作方式有问题,那就换一种操作方式。unity有提供定义好的第一人称控制器,那我去修改一下控制器的代码,变成我需要的代码即可。
在研究了unity自带的控制器代码之后,我发现unity对于移动物体的方式是,对需要移动的物体施加力来进行移动。通过获取物体的rigidbody属性,然后通过AddForce的方式,来对物体进行移动。并且找到了控制器是通过StickToGroundHelper方法将物体固定在地板上,原理是物体向下发射一条射线,如果射线的长度不为0,物体会向下运动。那么是要将StickToGroundHelper方法去除,剩下的代码进行保留,那么就可以实现最基本的无重力第一人称视角。再通过自定义按键,给物体添加向上或者向下的力,来控制物体的向上或者向下运动,就能实现第一人称无重力控制器。
最终的实现代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
public Transform target;
private float sliderMouseValue = 0.5f;
public bool isScaleFabric = false;
public float keyboardSpeed = 2f;
[Serializable]
public class MovementSettings
{
public float ForwardSpeed = 2.0f;
// Speed when walking forward
public float BackwardSpeed = 2.0f;
// Speed when walking backwards
public float StrafeSpeed = 2.0f;
// Speed when walking sideways
[HideInInspector]
public float CurrentTargetSpeed = 8f;
public void UpdateDesiredTargetSpeed(Vector2 input)
{
if (input == Vector2.zero) return;
if (input.x > 0 || input.x < 0)
{
//strafe
CurrentTargetSpeed = StrafeSpeed;
}
if (input.y < 0)
{
//backwards
CurrentTargetSpeed = BackwardSpeed;
}
if (input.y > 0)
{
//forwards
//handled last as if strafing and moving forward at the same time forwards speed should take precedence
CurrentTargetSpeed = ForwardSpeed;
}
}
}
public Camera cam;
public MovementSettings movementSettings = new MovementSettings();
public MouseLook mouseLook = new MouseLook();
private Rigidbody m_RigidBody;
private void Start()
{
m_RigidBody = GetComponent<Rigidbody>();
mouseLook.Init(transform, cam.transform);
}
private void Update()
{
if (isOpenCameraMove && !isScaleFabric && panelS.canEdit)
{
if (Cursor.visible == true) {
Cursor.visible = false;
}
RotateView();
}
if (Input.GetKeyDown(KeyCode.Z) && !isScaleFabric && panelS.canEdit)
{
inputKeyDownZ ();
}
}
private void inputKeyDownZ () {
isOpenCameraMove = !isOpenCameraMove;
//
鼠标显示隐藏
if (isOpenCameraMove) {
shadowAlphaTween.gameObject.SetActive (true);
shadowAlphaTween.PlayReverse ();
Cursor.visible = false;
} else {
shadowAlphaTween.PlayForward ();
StartCoroutine (HidePanel (shadowAlphaTween.gameObject));
#if UNITY_STANDALONE_WIN
SetCursorPos((int) panelS.device_width/2,(int)panelS.device_height/2);
#endif
Cursor.visible = true;
}
}
private void FixedUpdate()
{
if (isOpenCameraMove && target && !isScaleFabric && panelS.canEdit)
{
Vector2 input = GetInput();
//
if ((Mathf.Abs(input.x) > float.Epsilon || Mathf.Abs(input.y) > float.Epsilon) /*&& (advancedSettings.airControl || m_IsGrounded)*/)
//
{
// always move along the camera forward as it is the direction that it being aimed at
Vector3 desiredMove = cam.transform.forward * input.y + cam.transform.right * input.x;
desiredMove.x = desiredMove.x * movementSettings.CurrentTargetSpeed;
desiredMove.y = desiredMove.y * movementSettings.CurrentTargetSpeed;
desiredMove.z = desiredMove.z * movementSettings.CurrentTargetSpeed;
if (m_RigidBody.velocity.sqrMagnitude <
(movementSettings.CurrentTargetSpeed * movementSettings.CurrentTargetSpeed))
{
m_RigidBody.AddForce(desiredMove, ForceMode.Impulse);
}
//
}
m_RigidBody.drag = 5f;
//
if (Mathf.Abs(input.x) < float.Epsilon && Mathf.Abs(input.y) < float.Epsilon && m_RigidBody.velocity.magnitude < 1f)
//
{
//
m_RigidBody.Sleep();
//
}
if (Input.GetKey(KeyCode.Q)) {
m_RigidBody.AddForce(new Vector3(0f, keyboardSpeed, 0f), ForceMode.Impulse);
}
if (Input.GetKey(KeyCode.E))
{
m_RigidBody.AddForce(new Vector3(0f, -keyboardSpeed, 0f), ForceMode.Impulse);
}
}
}
private Vector2 GetInput()
{
Vector2 input = new Vector2
{
x = CrossPlatformInputManager.GetAxis("Horizontal"),
y = CrossPlatformInputManager.GetAxis("Vertical") + Input.GetAxis("Mouse ScrollWheel") * 2
};
movementSettings.UpdateDesiredTargetSpeed(input);
return input;
}
private void RotateView()
{
//avoids the mouse looking if the game is effectively paused
if (Mathf.Abs (Time.timeScale) < float.Epsilon)
return;
// get the rotation before it's changed
float oldYRotation = transform.eulerAngles.y;
mouseLook.LookRotation (transform, cam.transform);
// Rotate the rigidbody velocity to match the new direction that the character is looking
Quaternion velRotation = Quaternion.AngleAxis (transform.eulerAngles.y - oldYRotation, Vector3.up);
m_RigidBody.velocity = velRotation * m_RigidBody.velocity;
}
public void changeMouseSpeed (UISlider slider) {
//
this.movementSettings.StrafeSpeed = 0.5f + slider.value * 6f;
//
this.movementSettings.ForwardSpeed = 1f + slider.value * 10f;
//
this.movementSettings.BackwardSpeed = 0.5f + slider.value * 6f;
mouseLook.XSensitivity = 0.1f + 1.9f * slider.value;
mouseLook.YSensitivity = 0.1f + 1.9f * slider.value;
}
ps:如果需要更改视角的旋转速度,可以参考代码中的changeMouseSpeed方法,另外如果移动速度过快,肯能会导致unity来不及检测碰撞进行处理,最终使得物体仍然穿越墙壁。建议速度不要过大,另外也可以打开unity中的 Edit –> Project Settings –> Time 的属性面板,通过调节Fixed Timestep属性值,来缩短检测时间。
最后
以上就是苗条外套最近收集整理的关于Unity 之第一人称无重力控制器的全部内容,更多相关Unity内容请搜索靠谱客的其他文章。
发表评论 取消回复