unity Mirror使用心得一(玩家角色创建,控制,及其攻击其他玩家的血量同步设置)
Posted MoonWinding
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了unity Mirror使用心得一(玩家角色创建,控制,及其攻击其他玩家的血量同步设置)相关的知识,希望对你有一定的参考价值。
先分享下个人mirrordemo 的github :
https://github.com/IsaWinding/MirrorDemo.git
mirror 的官方下载地址:
https://assetstore.unity.com/packages/tools/network/mirror-129321
1.添加NetworkManager
2.添加NetworkMangerHud
3.制作玩家角色预制体并添加mirror 相关组件
networkTransform 和networkAnimator ClientAuthority 需要勾选
分享玩家操作脚本
using Mirror;
using Prime31;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterInput : NetworkBehaviour
public float moveSpeed = 8f;
public float jumpSpeed = 20f;
public float rayDistance = 4f;
public float atkDistance = 2f;
public float atk = 10f;
public float atkOffset = 1f;
public bool isFaceRight = true;
public KeyCode moveLeftKey;
public KeyCode moveRightKey;
public KeyCode attackKey;
public KeyCode jumpKey;
public LayerMask platformMask = 0;
public LayerMask enemyMask = 0;
private bool isMove = false;
private bool isJump = false;
private bool isOnGround = true;
private Vector3 moveDelta;
private bool isAttack = false;
private bool curIsFaceRight = true;
private Rigidbody2D body;
private Vector3 oriScale;
private NetworkIdentity identity;
private CharacterAni characterAni;
private CharacterHp characterHp;
public int MaxHp = 100;
[SyncVar(hook = "HpChange")]
public int curHp = 100;
public NetworkIdentity Identity get return identity;
void Awake()
body = this.gameObject.GetComponent<Rigidbody2D>();
oriScale = this.transform.localScale;
identity = this.gameObject.GetComponent<NetworkIdentity>();
characterAni = this.gameObject.GetComponent<CharacterAni>();
characterHp = this.gameObject.GetComponent<CharacterHp>();
characterHp.SetHpInfo(curHp, MaxHp);
private void Start()
if(identity.isLocalPlayer)
CameraSmoothFollow.Instance.SetTarget(this.transform);
private bool isCanReborn = true;
private void OnDead()
if (isCanReborn)
Invoke("Reborn", 5f);
isCanReborn = false;
[Command]
private void Reborn()
curHp = MaxHp;
SetHpInfo();
RpcReborn();
[ClientRpc]
private void RpcReborn()
this.transform.localPosition = Vector3.zero;
isCanReborn = true;
public bool IsDead()
return curHp <= 0;
// the Update loop contains a very simple example of moving the character around and controlling the animation
private void Update()
if (!identity.isLocalPlayer)
return;
if (IsDead())
return;
isMove = false;
moveDelta = Vector3.zero;
isOnGround = Physics2D.Raycast(this.transform.position, Vector2.down, rayDistance, platformMask);
if (isOnGround && Input.GetKeyDown(jumpKey))
isJump = true;
if (Input.GetKeyDown(attackKey))
isAttack = true;
if (Input.GetKey(moveLeftKey))
isMove = true;
moveDelta.x = -moveSpeed;
else if (Input.GetKey(moveRightKey))
isMove = true;
moveDelta.x = moveSpeed;
public void HpChange(int pOld,int hp)
characterHp.SetHpInfo(hp, MaxHp);
if (hp <= 0)
OnDead();
public void SetHpInfo()
characterHp.SetHpInfo(curHp, MaxHp);
public void OnDamage(int pDamage)
curHp -= pDamage;
if (curHp > MaxHp)
curHp = MaxHp;
if (curHp < 0)
curHp = 0;
SetHpInfo();
private bool IsInAtkRange(Transform pTarget)
var direction = curIsFaceRight ? Vector2.right* atkDistance : -Vector2.right* atkDistance;
var targetPos = pTarget.position;
var selfPos = this.transform.position;
if (Mathf.Abs(targetPos.y - selfPos.y) <= atkOffset && Mathf.Abs(targetPos.x - (selfPos.x + direction.x)) <= atkOffset)
return true;
return false;
[Command]
public void CmdDoNormalAttack()
//Debug.LogError("CmdDoNormalAttack");
var direction = curIsFaceRight ? Vector2.right : -Vector2.right;
var identitys = GameObject.FindObjectsOfType<CharacterInput>(false);
for (var i = 0; i < identitys.Length; i++)
if(identitys[i] != this)
if (IsInAtkRange(identitys[i].transform))
identitys[i].OnDamage((int)atk);
var ai = GameObject.FindObjectsOfType<MonsterAI>(false);
for (var i = 0; i < ai.Length; i++)
if (ai[i] != this)
if (IsInAtkRange(ai[i].transform))
ai[i].OnDamage((int)atk);
[Command]
void CmdSetCurFaceRight(bool pSetRight)
curIsFaceRight = pSetRight;
private void FixedUpdate()
if (!identity.isLocalPlayer)
return;
if (IsDead())
return;
if (isAttack)
characterAni.PlayAni("attack",4,()=>
isAttack = false;
CmdDoNormalAttack();
);
if (isMove)
this.transform.localPosition += moveDelta * Time.deltaTime;
int xS = isFaceRight == moveDelta.x > 0 ? 1 : -1;
curIsFaceRight = xS == 1;
CmdSetCurFaceRight(curIsFaceRight);
this.transform.localScale = new Vector3(oriScale.x * xS, oriScale.y, oriScale.z);
characterAni.PlayAni("move", 3);
if (isJump)
body.AddForce(new Vector2(0,jumpSpeed));
isJump = false;
if (!isAttack && !isMove)
characterAni.PlayAni("idle", 1);
注意玩家进行攻击其他玩家的操作需要在服务器端运行,下面这段代码需要加上Command,加上Command的话是客户端在服务器段的玩家主体会运行该段逻辑,服务器改变后的血量信息通过
[SyncVar(hook = “HpChange”)]
public void HpChange(int pOld,int hp)
characterHp.SetHpInfo(hp, MaxHp);
if (hp <= 0)
OnDead();
同步的方法在每个客户端同步血量信息
[Command]
public void CmdDoNormalAttack()
//Debug.LogError("CmdDoNormalAttack");
var direction = curIsFaceRight ? Vector2.right : -Vector2.right;
var identitys = GameObject.FindObjectsOfType<CharacterInput>(false);
for (var i = 0; i < identitys.Length; i++)
if(identitys[i] != this)
if (IsInAtkRange(identitys[i].transform))
identitys[i].OnDamage((int)atk);
var ai = GameObject.FindObjectsOfType<MonsterAI>(false);
for (var i = 0; i < ai.Length; i++)
if (ai[i] != this)
if (IsInAtkRange(ai[i].transform))
ai[i].OnDamage((int)atk);
角色动画播放添加了一个攻击动画播放完的事件用来驱动攻击伤害计算逻辑
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterAni : MonoBehaviour
private Animator animator;
private string curAniName;
private int curAniProx = 0;
private void Awake()
animator = this.gameObject.GetComponent<Animator>();
private System.Action onFinish;
public bool PlayAni(string pAniName,int pProx,System.Action pOnFinish = null)
if (curAniName != null && IsPlayAning(curAniName) && pProx <= curAniProx)
return false;
animator.CrossFade(pAniName,0);
curAniName = pAniName;
curAniProx = pProx;
onFinish = pOnFinish;
return true;
private bool IsPlayAning(string pAniName)
AnimatorStateInfo animatorInfo = animator.GetCurrentAnimatorStateInfo(0);
if ((animatorInfo.normalizedTime <= 1.0f) && (animatorInfo.normalizedTime > 0f) && (animatorInfo.IsName(pAniName)))
return true;
return false;
public void OnAttackFinish()
if (onFinish != null)
onFinish.Invoke();
血条显示设置
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterHp : MonoBehaviour
public SpriteRenderer hpBg;
public SpriteRenderer hp;
public int maxHp = 100;
public float curHp = 100f;
private float oriScale = 8;
void Awake()
oriScale = hpBg.transform.localScale.x;
public void SetHpInfo(int pCurHp,int pMaxHp)
curHp = pCurHp;
maxHp = pMaxHp;
SetCurHp();
private void SetCurHp()
var hpProgress = curHp / maxHp;
hp.transform.localScale = new Vector3(hpProgress * oriScale,1,1);
hp.transform.localPosition = new Vector3(-(1- hpProgress) * oriScale/2, 0,0);
一下篇介绍服务器怪物的运行和创建
以上是关于unity Mirror使用心得一(玩家角色创建,控制,及其攻击其他玩家的血量同步设置)的主要内容,如果未能解决你的问题,请参考以下文章
Unity入门计划Unity2D动画-脚本与混合树实现玩家角色动画过渡
Ruby‘s Adventrue游戏制作笔记Unity角色攻击——发射子弹