unity Mirror使用心得一(玩家角色创建,控制,及其攻击其他玩家的血量同步设置)

21 篇文章 1 订阅
17 篇文章 0 订阅

先分享下个人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);
    }
}

一下篇介绍服务器怪物的运行和创建

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Unity2D中使用鼠标控制玩家攻击方向,您可以按照以下步骤进行操作: 1. 创建一个空物体作为玩家角色,并将其添加到场景中。 2. 在空物体上添加一个Sprite Renderer组件,用于显示玩家的外观。 3. 添加一个Box Collider 2D组件,使玩家能够与其他物体进行交互。 4. 创建一个空物体作为鼠标指针,并将其添加到场景中。 5. 在鼠标指针上添加一个Sprite Renderer组件,用于显示鼠标指针的外观。 6. 在鼠标指针上添加一个Box Collider 2D组件,使其具有交互性。 7. 创建一个C#脚本,将其添加到玩家角色上。 8. 在脚本中添加一个Update()方法,以便每帧都能更新玩家攻击方向。 9. 在Update()方法中,使用Input.mousePosition获取鼠标在屏幕上的位置。 10. 将鼠标位置转换为世界坐标,使用Camera.ScreenToWorldPoint()方法。 11. 计算从玩家角色到鼠标指针的向量,使用Vector2.Subtract()方法。 12. 将向量转换为角度,使用Mathf.Atan2()方法。 13. 将角度转换为欧拉角,使用Quaternion.Euler()方法。 14. 将欧拉角设置玩家角色的旋转,使用Transform.rotation属性。 15. 在脚本中添加一个攻击方法,使玩家能够在当前方向上攻击。 16. 将攻击方法绑定到适当的玩家输入,例如按下鼠标左键。 17. 在攻击方法中,创建一个攻击物体,并使其朝着当前方向移动。 18. 调整攻击物体的大小、速度和伤害,以适应您的游戏需求。 19. 在攻击物体与其他物体碰撞时,触发适当的逻辑,例如减少敌人的生命值。 通过以上步骤,您可以实现鼠标控制玩家攻击方向的功能。请注意,这只是一个基本示例,并且可能需要进行调整和优化,以适应您的具体游戏需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值