ARPG简单游戏原型

主要制作以下三个系统:

移动系统

动画系统

碰撞系统

 

一、移动系统

 

这里采用第三人称视角的角色移动方式,角色通过Character controller角色控制器进行移动,摄像机随角色转向一同转向,同时可以进行缩放操作

 

input.cs

输入控制器采用单例模式,与角色控制器脚本进行分离,让角色控制器可以通过这里的参数直接进行条件判断

public class InputC : MonoBehaviour
{
    private static InputC instance;

    public Vector3 m_Movement;  //存储x,z移动
    public bool m_atkTrigger;
    public Vector3 m_CamRot;  //存储摄像机参数

    public static InputC GetInstance()
    {
        return instance;
    }

    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }
    }

    // Update is called once per frame
    void Update()
    {
        m_Movement = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized;
        m_CamRot = new Vector3(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"),Input.GetAxis("Mouse ScrollWheel")); //x,y存储鼠标X轴和Y轴的移动,z存储滑轮滚动
        //m_atkTrigger = Input.GetMouseButtonDown(0);
    }
}

 

Player.cs

public class Player : MonoBehaviour
{
    CharacterController cc;
    Animator anim;
    public float moveSpeed = 5;
    public bool isAtking;
    // Start is called before the first frame update
    void Start()
    {
        cc = GetComponent<CharacterController>();
        anim = GetComponentInChildren<Animator>();
    }

    // Update is called once per frame
    void Update()
    {
        Move();
        Atk();
    }

    void Move()
    {
        anim.SetFloat("xSpeed", InputC.GetInstance().m_Movement.x); //通过单例的InputC访问到移动参数
        anim.SetFloat("zSpeed", InputC.GetInstance().m_Movement.z);
        Vector3 dir = transform.TransformDirection(InputC.GetInstance().m_Movement); //从局部坐标转换到世界坐标 因为SimpleMove需要世界坐标
        cc.SimpleMove(dir* moveSpeed*Time.deltaTime*200);
    }
    
    /*
    void Atk()
    {
        if (InputC.GetInstance().m_atkTrigger && !isAtking)
        {
            anim.SetTrigger("attack");
        }
    }
    */
}

 

CameraC.cs

public class CameraC : MonoBehaviour
{
    public Transform target;
    public Vector3 offset;
    public float distance; //摄像机缩放距离
    // Start is called before the first frame update

    public float x, y;//用于接收鼠标滑动
    public float rotSpeedX=100; //x鼠标旋转速度
    public float rotSpeedY=100; //y旋转速度
    public float zoomSpeed =100; //滚轮控制的相机缩放速度

    public float yLimitMin=-10, yLimitMax=70; //上下看时的视角限制
    public float disMin = 2, disMax = 10;  //滚轮缩放限制
    void Start()
    {
        Cursor.lockState = CursorLockMode.Locked;
        //offset = target.position - transform.position;
    }

    //摄像机通常写在LateUpdate 防止一些抖动
    private void LateUpdate()
    {
        x += InputC.GetInstance().m_CamRot.x * rotSpeedX * Time.deltaTime; //获取InputC存储的相机参数
        y -= InputC.GetInstance().m_CamRot.y * rotSpeedY * Time.deltaTime;

        y = clampAngle(y, yLimitMin, yLimitMax);
        transform.rotation = Quaternion.Euler(y, x, 0); //注意这里顺序是y,x 因为鼠标的左右移动实际对应的是y轴的旋转 因此这里的x在y的位置
        //transform.eulerAngles = new Vector3(y, x, 0); //第二种写法

        if (InputC.GetInstance().m_Movement.x != 0 || InputC.GetInstance().m_Movement.z != 0)
            target.rotation = Quaternion.Euler(0, x, 0); //相机旋转的同时将角色也旋转 保证角色y轴旋转方向和相机的y轴旋转方向一致

        //设置缩放距离
        distance -= (InputC.GetInstance().m_CamRot.z * Time.deltaTime) * zoomSpeed * Mathf.Abs(distance);
        //乘上一个distance绝对值让缩放效果更好 有一个渐快渐慢的效果
        distance = clampAngle(distance, disMin, disMax);
        
        //更新相机位置为 角色位置+offset偏移量(是否添加偏移可以自己设置) + 相机的旋转角度的向量值乘上距离(相当于将相机往旋转角度方向后移)
        transform.position = target.position + offset + transform.rotation * (new Vector3(0, 0, -1)) * distance;
    }
    
    //限制角度
    float clampAngle(float angle,float min,float max)
    {
        if (angle > 360) angle -= 360;
        else if (angle < -360) angle += 360;

        return Mathf.Clamp(angle, min, max);
    }
}

 

二、动画系统

 

动画系统的脚本代码主要来自于Animator的一个转换条件的设置。我们直接在Player中添加响应的转换条件代码设置就可以

其中移动动画的判断需要x、z方向的速度两个参数

攻击动画 需要一个attack的trigger

 

参数设置如下:

状态机面板连接如下:

 (就提一下idle->移动混合树的 转换条件,像这样四个或的判断)

 

修改InputC.cs

    void Update()
    {
        m_Movement = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized;
        m_CamRot = new Vector3(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"),Input.GetAxis("Mouse ScrollWheel")); //x,y存储鼠标X轴和Y轴的移动,z存储滑轮滚动
        m_atkTrigger = Input.GetMouseButtonDown(0); //用于判断是否点击鼠标左键  注意这里要有Down!!!
    }

 

修改Player.cs,添加Atk攻击判断脚本

void Move()
    {
        anim.SetFloat("xSpeed", InputC.GetInstance().m_Movement.x); //通过单例的InputC访问到移动参数
        anim.SetFloat("zSpeed", InputC.GetInstance().m_Movement.z);
        Vector3 dir = transform.TransformDirection(InputC.GetInstance().m_Movement); //从局部坐标转换到世界坐标 因为SimpleMove需要世界坐标
        cc.SimpleMove(dir* moveSpeed*Time.deltaTime*200);
    }

void Atk()
    {
        if (InputC.GetInstance().m_atkTrigger && !isAtking)
        {
            anim.SetTrigger("attack");
        }
    }

 

三、碰撞系统

 

这个原型的一个主要碰撞检测来自于我们的武器和敌人之间的碰撞,当使用onTriggerEnter的时候,两个物体都能访问,为了提高扩展性,我们是选择在敌人对象这边施加伤害的数据。同时提供了一个接口给敌人用于传递受伤的参数。

同时为了角色武器不会在任何时候都能发生碰撞,我们通过在动画中添加事件的方式来进行碰撞的开关

 

WeaponC.cs  挂载在武器对象上

public class weaponC : MonoBehaviour
{
    private void OnTriggerEnter(Collider other)
    {
        Debug.Log("hit");
        Ifight fi = other.GetComponent<Ifight>(); //通过获取敌人的接口来判断是否能施加伤害
        if (fi!=null)
        {
            fi.beHit(10); //调用敌人身上的beHit函数
            Debug.Log("hit");
        }
    }
}

武器的一些属性可以写在这里 如攻击力,属性效果(冰冻、火焰等等) 

 

MonsterC.cs

public class MonsterC : MonoBehaviour,Ifight  //继承接口类,接口需要写在父类后面
{
    public float HP = 30;
    public void beHit(int damage) //beHit是写在接口的函数
    {
        Debug.Log(name + "HP-" + damage);
        HP -= damage;
        if (HP <= 0)
        {
            Destroy(this.gameObject);
        }
    }
}

 

Ifight.cs 接口类

public interface Ifight
{
    public void beHit(int damage);
}

 

EventC.cs  动画事件响应写在这里

public class EventC : MonoBehaviour
{
    public Collider weaponColl;
    public Player player;
    
    //开启碰撞
    public void AtkEnable() 
    {
        weaponColl.enabled = true;
    }
    
    //关闭盘碰撞
    public void AtkDisable()
    {
        weaponColl.enabled = false;
    }
    
    //攻击开始判断 防止多次点击攻击触发
    public void AtkStart()
    {
        player.isAtking = true;
    }

    public void AtkEnd()
    {
        player.isAtking = false;
    }
}

 

在Animation窗口中添加动画事件

 

 

posted @   fjnloo  阅读(119)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示