前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PUN ☀️六、机器人基础设置:运动、相机、攻击与生命值

PUN ☀️六、机器人基础设置:运动、相机、攻击与生命值

作者头像
星河造梦坊官方
发布2024-08-15 21:09:35
550
发布2024-08-15 21:09:35
举报
文章被收录于专栏:星河造梦坊专栏

🟥 效果展示

首先,将PUN设为离线模式,先来进行本地测试。等发布时,再取消勾选改为联网模式。

为什么设置,可参考:传送门

🟧 机器人运动

Robot Kyle 从Assets拖到层级面板,进行如下配置:

1️⃣ 状态机及状态机控制

a、指定机器人状态机:Kyle Robot

b、挂载如下代码:

该代码负责控制机器人运动,WAD运动,跑起来后右键跳跃。

代码语言:javascript
复制
using Photon.Pun;
using UnityEngine;
 
public class PlayerAnimatorManager : MonoBehaviourPun
{
    #region Private Fields
 
    [SerializeField]
    float directionDampTime = 0.25f;
    Animator animator;
 
    #endregion
 
 
    #region Mono CallBacks
 
    void Start()
    {
        animator = GetComponent<Animator>();
    }
 
    void Update()
    {
        //教程参考:https://skode.blog.csdn.net/article/details/106356112
        if (PhotonNetwork.IsConnected == true && photonView.IsMine == false)
            return;
 
        AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
 
        // 只有在我们跑步的时,按下右键才允许跳跃。
        if (stateInfo.IsName("Base Layer.Run") && Input.GetButtonDown("Fire2"))
            animator.SetTrigger("Jump");
 
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical") < 0 ? 0 : Input.GetAxis("Vertical");
 
        animator.SetFloat("Speed", h * h + v * v);
        animator.SetFloat("Direction", h, directionDampTime, Time.deltaTime);
    }
 
    #endregion
}

2️⃣ 添加角色控制器

为机器人添加 CharacterController,调整Center等,属性如下:

3️⃣ 制作预制体

创建 Resources 文件夹,将Robot Kyle拖到Resources制成预制体。

🟨 相机跟随

为机器人添加如下脚本,勾选 FollwOnStart

该脚本的FollowOnStart,为离线模式,该功能是在离线模式下,让摄像机自动找到人物跟随。在发布时的联网状态下,需取消勾选,自己写代码判断哪个人物是自己的,来跟随。

你的Camera要为 MainCamera

代码语言:javascript
复制
using UnityEngine;
 
// Camera work. Follow a target
public class Skode_CameraWork : MonoBehaviour
{
    #region  Fields
 
    [Tooltip("在局部x-z平面到目标的距离")]
    public float distance = 7.0f;
 
    [Tooltip("我们希望相机高于目标的高度")]
    public float height = 3.0f;
 
    [Tooltip("相机高度的平滑时滞")]
    public float heightSmoothLag = 0.3f;
 
    [Tooltip("允许相机垂直于目标,例如,提供更多的景色和较少的地面")]
    public Vector3 centerOffset = Vector3.zero;
 
    [Tooltip("如果预制组件被光子网络改变,则将此设置为false,并在需要时手动调用OnStartFollowing()")]
    public bool followOnStart = false;
 
    // cached transform of the target
    Transform cameraTransform;
 
    // maintain a flag internally to reconnect if target is lost or camera is switched
    bool isFollowing;
 
    // Represents the current velocity, this value is modified by SmoothDamp() every time you call it.
    float heightVelocity;
 
    // Represents the position we are trying to reach using SmoothDamp()
    float targetHeight = 100000.0f;
 
    #endregion
 
 
    #region Mono Callbacks
 
    void Start()
    {
        // Start following the target if wanted.
        if (followOnStart)
            OnStartFollowing();
    }
 
    void LateUpdate()
    {
        // The transform target may not destroy on level load,
        // so we need to cover corner cases where the Main Camera is different everytime we load a new scene, and reconnect when that happens
        if (cameraTransform == null && isFollowing)
            OnStartFollowing();
 
        // only follow is explicitly declared
        if (isFollowing)
            Apply();
    }
 
    #endregion
 
 
    #region Public Methods
 
    /// <summary>
    /// Raises the start following event.
    /// Use this when you don't know at the time of editing what to follow, typically instances managed by the photon network.
    /// </summary>
    public void OnStartFollowing()
    {
        cameraTransform = Camera.main.transform;
        isFollowing = true;
        // we don't smooth anything, we go straight to the right camera shot
        Cut();
    }
 
    #endregion
 
 
    #region Private Methods
 
    /// <summary>
    /// Follow the target smoothly
    /// </summary>
    void Apply()
    {
        Vector3 targetCenter = transform.position + centerOffset;
        // Calculate the current & target rotation angles
        float originalTargetAngle = transform.eulerAngles.y;
        float currentAngle = cameraTransform.eulerAngles.y;
        // Adjust real target angle when camera is locked
        float targetAngle = originalTargetAngle;
        currentAngle = targetAngle;
        targetHeight = targetCenter.y + height;
 
        // Damp the height
        float currentHeight = cameraTransform.position.y;
        currentHeight = Mathf.SmoothDamp(currentHeight, targetHeight, ref heightVelocity, heightSmoothLag);
        // Convert the angle into a rotation, by which we then reposition the camera
        Quaternion currentRotation = Quaternion.Euler(0, currentAngle, 0);
        // Set the position of the camera on the x-z plane to:
        // distance meters behind the target
        cameraTransform.position = targetCenter;
        cameraTransform.position += currentRotation * Vector3.back * distance;
        // Set the height of the camera
        cameraTransform.position = new Vector3(cameraTransform.position.x, currentHeight, cameraTransform.position.z);
        // Always look at the target
        SetUpRotation(targetCenter);
    }
 
    /// <summary>
    /// Directly position the camera to a the specified Target and center.
    /// </summary>
    void Cut()
    {
        float oldHeightSmooth = heightSmoothLag;
        heightSmoothLag = 0.001f;
        Apply();
        heightSmoothLag = oldHeightSmooth;
    }
 
    /// <summary>
    /// Sets up the rotation of the camera to always be behind the target
    /// </summary>
    /// <param name="centerPos">Center position.</param>
    void SetUpRotation(Vector3 centerPos)
    {
        Vector3 cameraPos = cameraTransform.position;
        Vector3 offsetToCenter = centerPos - cameraPos;
        // Generate base rotation only around y-axis
        Quaternion yRotation = Quaternion.LookRotation(new Vector3(offsetToCenter.x, 0, offsetToCenter.z));
        Vector3 relativeOffset = Vector3.forward * distance + Vector3.down * height;
        cameraTransform.rotation = yRotation * Quaternion.LookRotation(relativeOffset);
    }
 
    #endregion
}

🟩 攻击系统

1️⃣ 激光射线

在机器人Head下新建如图所示两个cube作为激光射线

2️⃣ 碰撞器设置

只使用一个Collider就好,并作为触发器,避免作为碰撞器将别人碰飞

  • 将右眼激光的Collider移除,左眼Collider拉大包裹住两个cube
  • 勾选Collider的 isTrigger
  • 将带Collider射线的tag设为 beam

3️⃣ 激光控制

机器人添加下方脚本,并将Beams赋值给它

实现:当按下鼠标左键,打开激光。松开左键,关闭激光。

在网络中多人玩,还要考虑: 我按下了鼠标左键,那场景中的各个机器人,怎么判断我是属于谁,你按下鼠标左键我要不要执行程序? if (photonView.IsMine),便实现了此功能。判断这个机器人是不是我的。是的话,执行程序。

代码语言:javascript
复制
using UnityEngine;
using Photon.Pun;
 
public class PlayerManager : MonoBehaviour
{
    #region Parameters
 
    public GameObject beams;
 
    //当用户开火时,为True
    bool IsFiring;
 
    #endregion
 
 
    #region Mono CallBacks
 
    void Awake()
    {
        beams.SetActive(false);
    }
 
    void Update()
    {
        if (PhotonNetwork.IsConnected == true && photonView.IsMine)
            ProcessInputs();
 
        if (IsFiring != beams.activeSelf)
            beams.SetActive(IsFiring);
    }
 
    #endregion
 
 
    #region Private Methods
 
    void ProcessInputs()
    {
        //鼠标左键
        if (Input.GetButtonDown("Fire1"))
            IsFiring = true;
 
        if (Input.GetButtonUp("Fire1"))
            IsFiring = false;
    }
 
    #endregion
}

4️⃣ 预制体Apply一下

🟦 生命值系统

目标:

当射线击中时,扣0.1血,一直击中,每秒0.1。 当生命值0时,离开房间。

1、GameManager改为单例

2、PlayerManager 更新如下:

代码语言:javascript
复制
using UnityEngine;
using Photon.Pun;
 
public class PlayerManager : MonoBehaviourPunCallbacks
{
    #region Parameters
 
    public GameObject beams;
 
    public float Health = 1f;
 
    //当用户开火时,为True
    bool IsFiring;
 
    #endregion
 
 
    #region Mono CallBacks
 
    void Awake()
    {
        beams.SetActive(false);
    }
 
    void Update()
    {
        if (Health <= 0f)
            GameManager.ins.Skode_LeaveRoom();
 
        if (PhotonNetwork.IsConnected == true && photonView.IsMine)
            ProcessInputs();
 
        if (IsFiring != beams.activeSelf)
            beams.SetActive(IsFiring);
    }
    #endregion
 
 
    #region Private Methods
 
    void ProcessInputs()
    {
        //鼠标左键
        if (Input.GetButtonDown("Fire1"))
            IsFiring = true;
 
        if (Input.GetButtonUp("Fire1"))
            IsFiring = false;
    }
 
    void OnTriggerEnter(Collider other)
    {
        if (!photonView.IsMine && !other.CompareTag("beam"))
            return;
 
        Health -= 0.1f;
    }
 
    void OnTriggerStay(Collider other)
    {
        if (!photonView.IsMine && !other.CompareTag("beam"))
            return;
 
        //乘以增量时间,防止因为帧率FPS不同,扣血不同(举例:不乘,每帧执行一次扣血,卡的人比流畅的人扣血少)
        Health -= 0.1f * Time.deltaTime;
    }
    #endregion
}

大家还有什么问题,欢迎在下方留言!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-09-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 🟥 效果展示
  • 🟧 机器人运动
    • 1️⃣ 状态机及状态机控制
      • 2️⃣ 添加角色控制器
        • 3️⃣ 制作预制体
        • 🟨 相机跟随
        • 🟩 攻击系统
          • 1️⃣ 激光射线
            • 2️⃣ 碰撞器设置
              • 3️⃣ 激光控制
                • 4️⃣ 预制体Apply一下
                • 🟦 生命值系统
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档