本篇是对MultiPlayer项目的一些记录
public void Move(float horizontal, float vertical)
{
Vector3 movement = new Vector3(horizontal, 0f, vertical);
movement = movement.normalized * speed*0.025f;// * Time.deltaTime;
PlayerRigidbody.MovePosition(transform.position + movement);
}
注意这里的movement不能乘以deltaTime,因为要保证每一帧走的距离是一样的而不是随着帧率改变!!
否则再不同客户端会看到每一次Input在不同客户端上的距离不同(不同客户端的帧率不同),造成不同步!
Rigidbody.position和 Transform.position的区别:
Rigidbody.position
public Vector3 position;
Description
The position of the rigidbody.
Rigidbody.position allows you to get and set the position of a Rigidbody using the physics engine. If you change the position of a Rigibody using Rigidbody.position, the transform will be updated after the next physics simulation step. This is faster than updating the position using Transform.position, as the latter will cause all attached Colliders to recalculate their positions relative to the Rigidbody.
If you want to continuously move a rigidbody use MovePosition instead, which takes interpolation into account.
比如:
ViewManager.cs
if (msg.msg_type == (int)RequestType.INPUT)
{
InputMessage Input_msg = msg as InputMessage;
viewPlayer.get_Rigidbody().position = new Vector3(viewPlayer.get_Rigidbody().position.x,Input_msg.moving_y, viewPlayer.get_Rigidbody().position.z);
viewPlayer.Move(Input_msg.moving_x, Input_msg.moving_z);
}
如果使用:
viewPlayer.transform.position = new Vector3(viewPlayer.transform.position.x,Input_msg.moving_y, viewPlayer.transform.position.z);
如果环境复杂(有很多collider)就会卡住很久!
例如这种环境:

因为如果使用 Transform.position, 将导致所有附加的Collider重新计算它们相对于刚体的位置。https://docs.unity3d.com/ScriptReference/Transform-position.html
例如:简单的环境中:

viewPlayer.transform.position = new Vector3(viewPlayer.transform.position.x, Input_msg.moving_y, viewPlayer.transform.position.z);
用transform完全没有问题
RigidBody.position在下一次update时就会改变。
https://docs.unity3d.com/ScriptReference/Rigidbody-position.html
box colider不会穿过Terrian collider,但如果player是capsule collider就会穿过去 = =不清楚为啥
最终的Terrian设置和player设置

代码更改的地方:


Rigidbody.freezeRotation 冻结旋转
var freezeRotation : bool
Description描述
Controls whether physics will change the rotation of the object.
控制物理是否改变物体的旋转。
If freezeRotation is enabled, the rotation is not modified by the physics simulation. This is useful for creating first person shooters, because the player needs full control of the rotation using the mouse.
如果freezeRotation被启用,旋转不会被物体模拟修改。这对于创建第一人称射击游戏时是很有用的,因为玩家需要使用鼠标完全控制旋转。
using UnityEngine;
using System.Collections;
public class example : MonoBehaviour {
public void Awake() {
rigidbody.freezeRotation = true;
}
}
Input.GetMouseButtonDown
public static bool GetMouseButtonDown(int button);
Description
Returns true during the frame the user pressed the given mouse button.
You need to call this function from the Update function, since the state gets reset each frame. It will not return true until the user has released the mouse button and pressed it again. button values are 0 for the primary button (often the left button), 1 for secondary button, and 2 for the middle button.
using UnityEngine;
using System.Collections;
// Detects clicks from the mouse and prints a message
// depending on the click detected.
public class ExampleClass : MonoBehaviour
{
void Update()
{
if (Input.GetMouseButtonDown(0))
Debug.Log("Pressed primary button.");
if (Input.GetMouseButtonDown(1))
Debug.Log("Pressed secondary button.");
if (Input.GetMouseButtonDown(2))
Debug.Log("Pressed middle click.");
}
}
RigidBody.AddForce()失效的原因:
Drag、Angular Drag设成了无穷大,所以给rigidbody推力时动不了,还要记得所有的碰撞要放在FixedUpdated里!
把Drag、Angular Drag改小就好

Player穿过地面的原因:(Player 和地面都添加了collider还时穿过了。。)
不能用RigidBody.MovePosition()!!必须通过AddForce让player运动!,还有所有RigidBody的运动都要放在FixedUpdate()里!!!
让Player贴着地面走
只需要给Player,y轴方向一个速度就行!和move_x,move_z一样的道理,相当于给Player一个重力,并不需要投一条射线再判断交点(卡死 = =)
PlayerRigidbody.AddForce((-Transform.forward) * horizontal * speed );
PlayerRigidbody.AddForce(Transform.right * vertical * speed);
PlayerRigidbody.AddForce((-Transform.up) * 1f);
最终代码:
public class ViewPlayer : MonoBehaviour
{
GameObject player_instance;
public int connectID;//与Player的connectID相同;
float speed = 20f;
float rotate_speed = 10f;
public GameObject BulletPrefabs;
public Transform bulletSpawn;
Rigidbody PlayerRigidbody;
List<CustomSyncMsg> msg_list;
int touchTerrianMask;
float max_y = 100f;
float z = 0;
float y = 0;
float last_y = 2f;
float x = 0;
float rot_x = 0;
float rot_y = 0;
int key_x1 = 0;
int key_x2 = 0;
Transform Transform;
public CameraFollow camera;
public void Start()
{
msg_list = new List<CustomSyncMsg>();
PlayerRigidbody = GetComponent<Rigidbody>();
touchTerrianMask = LayerMask.GetMask("touchTerrian");
Transform = gameObject.GetComponent<Transform>();
PlayerRigidbody.freezeRotation = true;
}
public void FixedUpdate()
{
x += Input.GetAxis("Horizontal");
z += Input.GetAxis("Vertical");
if (Input.GetKeyDown(KeyCode.Q))
{
key_x1++;
rot_x = 0;
}
if (Input.GetKeyDown(KeyCode.E))
{
key_x2++;
rot_x = 0;
}
if (Input.GetKeyUp(KeyCode.Q))
{
rot_x += key_x1 * (-rotate_speed);
key_x1 = 0;
}
if (Input.GetKeyUp(KeyCode.E))
{
rot_x += key_x2 * (rotate_speed);
key_x2 = 0;
}
}
public List<CustomSyncMsg> get_local_input()
{
msg_list.Clear();
if (x != 0 || z != 0)
{
CustomSyncMsg input_msg = new InputMessage(connectID, new Vector3(x, 0, z));
msg_list.Add(input_msg);
}
x = 0;
z = 0;
if (rot_x != 0f || rot_y != 0f)
{
CustomSyncMsg rot_msg = new RotateMessage(connectID, new Vector2(rot_x, rot_y));
msg_list.Add(rot_msg);
rot_x = 0;
rot_y = 0;
}
return msg_list;
}
public void Move(float horizontal, float vertical)//, float y
{
// transform.up =new Vector3(0,0,y);
//Transform.position = new Vector3(Transform.position.x, y, Transform.position.z);
// Vector3 cur_pos= Transform.position +(-Transform.forward )* horizontal * speed * 0.025f + Transform.right * vertical * speed * 0.025f +(- Transform.up) * 100f;
// PlayerRigidbody.MovePosition(cur_pos);
PlayerRigidbody.AddForce((-Transform.forward) * horizontal * speed );
PlayerRigidbody.AddForce(Transform.right * vertical * speed);
PlayerRigidbody.AddForce((-Transform.up) * 1f);
//Transform.position = new Vector3(Transform.position.x, y, Transform.position.z);
// Debug.Log("PlayerRigidbody.position + movement = " + (PlayerRigidbody.position + movement).x + "PlayerRigidbody.position + movement " + (PlayerRigidbody.position + movement).y);
//PlayerRigidbody.AddForce(transform.right * horizontal * speed, ForceMode.Impulse);
//PlayerRigidbody.AddForce(transform.forward * vertical * speed, ForceMode.Impulse);
}
public void Rotate(float delta_x, float delta_y)
{
//现在只有绕自己的y轴旋转
Transform.Rotate(Vector3.up, delta_x, Space.Self);
Tool.Print("......................Rotating .........delta_x = " + delta_x.ToString());
}
public float move_on_the_ground()
{
Ray ray = new Ray(transform.position, -transform.up);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, max_y, touchTerrianMask))
{
return hit.point.y + 1f;
}
else
return -101;
}
public void set_player_instance(GameObject instance)
{
player_instance = instance;
}
public float get_speed()
{
return speed;
}
public Rigidbody get_Rigidbody()
{
return PlayerRigidbody;
}
public void bind_cameraFollow(CameraFollow cameraFollow)
{
camera = cameraFollow;
}
public void bind_playerInstance(GameObject Instance)
{
player_instance = Instance;
}
}
Mathf.SmoothDamp
public static float SmoothDamp(float current, float target, ref float currentVelocity, float smoothTime, float maxSpeed = Mathf.Infinity, float deltaTime = Time.deltaTime);
Parameters
| current | The current position. |
| target | The position we are trying to reach. |
| currentVelocity | The current velocity, this value is modified by the function every time you call it. |
| smoothTime | Approximately the time it will take to reach the target. A smaller value will reach the target faster. |
| maxSpeed | Optionally allows you to clamp the maximum speed. |
| deltaTime | The time since the last call to this function. By default Time.deltaTime. |
Description
Gradually changes a value towards a desired goal over time.
The value is smoothed by some spring-damper like function, which will never overshoot. The function can be used to smooth any kind of value, positions, colors, scalars.
using UnityEngine;
public class Example : MonoBehaviour
{
// Smooth towards the height of the target
Transform target;
float smoothTime = 0.3f;
float yVelocity = 0.0f;
void Update()
{
float newPosition = Mathf.SmoothDamp(transform.position.y, target.position.y, ref yVelocity, smoothTime);
transform.position = new Vector3(transform.position.x, newPosition, transform.position.z);
}
}
Transform.rotation
Other Versions
public Quaternion rotation;
Description
A quaternion that stores the rotation of the Transform in world space.
Transform.rotation stores a quaternion. You can use rotation to rotate a GameObject or provide the current rotation. Do not attempt to edit/modify rotation. Transform.rotation is less than 180 degrees.
Transform.rotation has no gimbal lock.
To rotate a Transform, use Transform.Rotate, which uses Euler Angles.
using UnityEngine;
// Transform.rotation example.
// Rotate a GameObject using a Quaternion.
// Tilt the cube using the arrow keys. When the arrow keys are released
// the cube will be rotated back to the center using Slerp.
public class ExampleScript : MonoBehaviour
{
float smooth = 5.0f;
float tiltAngle = 60.0f;
void Update()
{
// Smoothly tilts a transform towards a target rotation.
float tiltAroundZ = Input.GetAxis("Horizontal") * tiltAngle;
float tiltAroundX = Input.GetAxis("Vertical") * tiltAngle;
// Rotate the cube by converting the angles into a quaternion.
Quaternion target = Quaternion.Euler(tiltAroundX, 0, tiltAroundZ);
// Dampen towards the target rotation
transform.rotation = Quaternion.Slerp(transform.rotation, target, Time.deltaTime * smooth);
}
}
transform.rotation范围为 -180到180度!!
鼠标的位置,就是player最终的位置!而不是旋转差!这样的好处是鼠标定在哪,player的旋转角度就固定在哪,否则,若是旋转差的话,会出现鼠标不能与枪口一致的情况
项目中的旋转代码:
//Rotate
readonly float rotate_speed = 4f;
float rot_x = 0;
float rot_y = 0;
int key_x1 = 0;
int key_x2 = 0;
float currentRotation = 0;
float currentVelocity =0.0f;//旋转到targetRaotation的速度
public float smoothTime=0.1f;//旋转到targetRaotation所需要的时间
float bottomAngle = -45f;
float topAngle = 60f;
readonly float camRayLength = 100f;
Vector3 playerToMouse;
public void Rotate(float delta_x, float delta_y)
{
// Transform.Rotate(Vector3.up, delta_x, Space.Self);
currentRotation = Mathf.SmoothDamp(currentRotation, delta_x, ref currentVelocity, smoothTime);
transform.rotation = Quaternion.Euler(0, currentRotation, 0);
//Quaternion targetRotation = new Quaternion(0, Mathf.Sin(delta_x / 2.0f), 0, Mathf.Cos(delta_x / 2.0f));//将角度转化为四元素
//Quaternion newRotation = Quaternion.Lerp(transform.rotation, targetRotation, Time.timeScale * 20.0f);
//PlayerRigidbody.MoveRotation(newRotation);
}
public void FixedUpdate()
{
PlayerRigidbody.AddForce(Physics.gravity * Mygravity, ForceMode.Acceleration);
x += Input.GetAxis("Horizontal");
z += Input.GetAxis("Vertical");
//RotateInput
rot_x += Input.GetAxis("Mouse X") * rotate_speed;
rot_y += Input.GetAxis("Mouse Y") * rotate_speed;
rot_y = Mathf.Clamp(rot_y, bottomAngle, topAngle);
if (Input.GetMouseButton(0))
{
has_shoot = true;
shootRay = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(shootRay.origin, shootRay.direction * 100, Color.yellow);
}
else
{
has_shoot = false;
}
if (isDead)
{
Death();
}
}
本文深入探讨Unity中MultiPlayer项目的关键技术,包括角色移动、旋转的精确控制,刚体 Rigidbody 的正确使用方法,以及如何避免不同客户端间因帧率差异造成的同步问题。特别强调了在复杂的碰撞环境下,Rigidbody.position 和 Transform.position 的区别及其性能影响,提供了在Unity中实现平滑、高效网络同步的实用代码示例。
2万+

被折叠的 条评论
为什么被折叠?



