动作游戏Demo(二)主角的控制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36215025/article/details/64438989

1.环境创建

  • 资源导入
    -Image.unitypackage (地板材质图片)
    -FX.unitypackage (下雨和雨滴特效)

  • 使用Cube创建环境

  • 使用FX中的特效给环境添加下雨的效果
    这里写图片描述

    2.摄像机的跟随

  • 开发摄像机的跟随

  • 使用Vertor3.Lerp插值计算
  • 摄像机的视角,让摄像机观察这角色
  • Quanternuin.LookRotation得到一个四元数,表示跟参数一样方向的旋转
  • Quanternion.Slerp按照圆弧插值,这个插值计算更适用于,角度变换
using UnityEngine;
using System.Collections;

public class FollowPlayer : MonoBehaviour {
    private Transform player;
    public float speed = 2;

    void Start() {
        player = GameObject.Find(Tags.player).transform;   //查找主角的Transform组件
    }

    void Update() {
        Vector3 targetPos = player.position + new Vector3(0f,2f,-4f);  //设定摄像机和主角之间的距离
        transform.position =Vector3.Lerp(transform.position,targetPos,speed*Time.deltaTime);
        Quaternion targetRotation = Quaternion.LookRotation(player.position-transform.position);
        transform.rotation = Quaternion.Slerp(transform.rotation,targetRotation,speed*Time.deltaTime);
    }
}

3使用CharacterController控制行走

  • 使用Input.GetAxis(“Horizontal”) 和 “Vertical”得到垂直和水平方向的值
  • 使用CharacterController.SimpleMove(Vector3)参数表示运动的方向和速度 单位可以认为是 m/s
using UnityEngine;
using System.Collections;

public class PlayerMove : MonoBehaviour {
    private CharacterController cc;
    public float speed = 40;
    private Animator animator;
    void Awake() {
       cc = transform.GetComponent<CharacterController>();
       animator = this.GetComponent<Animator>();
    }

    void Update () {
        float h = Input.GetAxis("Horizontal");  //按A的时候返回的是1 按D的时候返回的是-1
        float v = Input.GetAxis("Vertical");    //w返回1,s返回-1

        if (Mathf.Abs(h) > 0.1f || Mathf.Abs(v) > 0.1f)
        {    //加上判断主要是防止当没有按下时朝向LookAt为空
                Vector3 targetDir = new Vector3(h, 0, v);  //方向
                transform.LookAt(targetDir + transform.position); //朝着一个点(主角的坐标点加上一个方向)以主角的坐标为基准朝向一个点
                cc.SimpleMove(transform.forward * speed * Time.deltaTime);
            }
      }
}

4-添加虚拟杆控制行走

使用NGUI做虚拟杆
这里写图片描述
- 触摸位置的检测UICamera.lastTouchPosition
- 根据触摸位置的移动虚拟按钮
- 得到虚拟杆横向和垂直的值,控制角色的移动

using UnityEngine;
using System.Collections;

public class JoyStick : MonoBehaviour {

    private bool isPress = false;
    private Transform button;    //虚拟摇杆的小圆圈
    public static float h = 0;
    public static float v = 0;

    void Awake() {
        button = transform.Find(Tags.button);   //标签多的话可以单独设置一个Tags脚本存放,以免自己拼写错string
    }

    void OnPress(bool isPress) {
        this.isPress = isPress;
        if (isPress == false) {
            button.localPosition = Vector2.zero;   //抬起时让摇杆的小圆圈位置归零
            h = 0; v = 0;
        }
    }

    void Update () {
        if (isPress) {
            Vector2 touchPos = UICamera.lastTouchPosition;
            touchPos -= new Vector2(100,100);
            float distance = Vector2.Distance(Vector2.zero, touchPos);
            if (distance > 75){
                touchPos = touchPos.normalized * 75;
                button.localPosition = touchPos;
            }else {
                button.localPosition = touchPos;
            }
             h = touchPos.x / 75;
             v = touchPos.y / 75;
        }
    }
}
using UnityEngine;
using System.Collections;

public class PlayerMove : MonoBehaviour {
    private CharacterController cc;
    public float speed = 40;
    private Animator animator;
    void Awake() {
       cc = transform.GetComponent<CharacterController>();
       animator = this.GetComponent<Animator>();
    }

    void Update () {
        float h = Input.GetAxis("Horizontal");  //按A的时候返回的是1 按D的时候返回的是-1
        float v = Input.GetAxis("Vertical");    //w返回1,s返回-1
        if (JoyStick.h != 0 || JoyStick.v != 0){
            h = JoyStick.h;
            v = JoyStick.v;
        }
        if (Mathf.Abs(h) > 0.1f || Mathf.Abs(v) > 0.1f)
        {    //加上判断主要是防止当没有按下时朝向LookAt为空   
                Vector3 targetDir = new Vector3(h, 0, v);  //方向
                transform.LookAt(targetDir + transform.position); //朝着一个点(主角的坐标点加上一个方向)以主角的坐标为基准朝向一个点
                cc.SimpleMove(transform.forward * speed * Time.deltaTime);
        }
    }
}

5-主角的状态机设计

  • 使用Unity里面的Animation State Machine动画状态机
  • 动画的属性设置,分为循环动画和一次性动画
  • 主角的动画分类
  • 休闲
  • 行走
  • 攻击 Attack1 Attack2 RangeAttack
  • 被攻击

这里写图片描述
(对于walk和stand动画,需要多勾选LoopTime和LoopPose,剩下的只勾选三个BakeIntoPose【表示任何用上bake into pose的对应的属性(例如:root transform rotation),将会直接由角色或物件的自身transform的rotation来决定角色方向(如由script直接驱动角色transform rotation角度令角色转身,适合的动画是原地踏步),反之就是用animation 本身的信息来驱动角色transform rotation(适合动画是转身动画本身就带动角色转动,如转45度转身动画,90度转身动画等)于动画基于模型】)

using UnityEngine;
using System.Collections;
public class PlayerMove : MonoBehaviour {
    private CharacterController cc;
    public float speed = 40;
    private Animator animator;
    void Awake() {
       cc = transform.GetComponent<CharacterController>();
       animator = this.GetComponent<Animator>();
    }

    void Update () {
        float h = Input.GetAxis("Horizontal");  //按A的时候返回的是1 按D的时候返回的是-1
        float v = Input.GetAxis("Vertical");    //w返回1,s返回-1
        if (JoyStick.h != 0 || JoyStick.v != 0){
            h = JoyStick.h;
            v = JoyStick.v;
        }
        if (Mathf.Abs(h) > 0.1f || Mathf.Abs(v) > 0.1f)
        {    //加上判断主要是防止当没有按下时朝向LookAt为空
            animator.SetBool("Walk", true);  //行走动画
            if (animator.GetCurrentAnimatorStateInfo(0).IsName("PlayerRun"))  //加一层判断,确保动画与实际行走相吻合
            {
                Vector3 targetDir = new Vector3(h, 0, v);  //方向
                transform.LookAt(targetDir + transform.position); //朝着一个点(主角的坐标点加上一个方向)以主角的坐标为基准朝向一个点
                cc.SimpleMove(transform.forward * speed * Time.deltaTime);
            }
        }
        else {
            animator.SetBool("Walk",false);  //Idel动画
        } 
    }
}

6-攻击的动画控制

  • 设置虚拟的攻击按钮
  • 通过代码添加按钮的点击事件监听
    • EventDelegate normalClickEvent = new EventDelegate(this, “OnNormalAttackButtonClick”);
    • GameObject.Find(“NormalAttack”).GetComponent().onClick.Add(normalClickEvent);
  • 给状态机设置状态条件
    • 修改Trigger animator.SetTrigger(“AttackB”);
    • 修改Bool animator.SetBool(“Walk”, false);
      这里写图片描述
      在Player@PlayerAttackA动画文件添加两个事件,这两个事件必须要在脚本中实现监听,否则会运行错误public void AttackBEvent2()/public void AttackBEvent1()。 实现连招
using UnityEngine;
using System.Collections;

public class PlayerAnimationAttack : MonoBehaviour {

    private bool isCanAttackB;
    private Animator animator;
    void Start () {
        animator=this.GetComponent<Animator>();
        EventDelegate NormalAttackEvent = new EventDelegate(this,"OnNormalAttackClick");   //这个脚本上 的--方法
        GameObject.Find("Normal").GetComponent<UIButton>().onClick.Add(NormalAttackEvent);     //也可以在面板上赋值
        EventDelegate RangeAttackEvent = new EventDelegate(this,"OnRangeAttackClick");
        GameObject.Find("Range").GetComponent<UIButton>().onClick.Add(RangeAttackEvent);
        EventDelegate RedAttackEvent = new EventDelegate(this, "OnRedAttackClikc");
        GameObject go = GameObject.Find("RedAttack");
        go.GetComponent<UIButton>().onClick.Add(RedAttackEvent);
        go.SetActive(false);   //go.active=false api已经过时
    }

    // Update is called once per frame
    void Update () {

    }
    public void AttackBEvent1() {
        isCanAttackB = true;
    }
    public void AttackBEvent2() {
        isCanAttackB = false;
    }
    public void OnNormalAttackClick() {
        if (animator.GetCurrentAnimatorStateInfo(0).IsName("PlayerAttackA") && isCanAttackB)
        {  //按下两次normal 且在attackA播放区间
            animator.SetTrigger("AttackB");
        }
        else
        {
            animator.SetTrigger("AttackA");
        }
    }
    public void OnRangeAttackClick() {
        animator.SetTrigger("AttackRange");
    }
    public void OnRedAttackClikc() {
        animator.SetTrigger("AttackA");
    }
}

猜你喜欢

转载自blog.csdn.net/qq_36215025/article/details/64438989