当前位置:   article > 正文

Unity2d平台动作游戏教程_physics2d.overlapcircle

physics2d.overlapcircle

最终效果如下:

目录

玩家的基础移动

加入移动和跳跃动画

加入攻击动作

实现打击感

添加敌人受击动画与击退

添加敌人受击特效

 攻击时的屏幕振动


首先实现最基础的功能,将环境拖拽进来,注意到环境的各个前后景的层次感是通过设定layer实现的:

 

玩家的基础移动

我们将玩家设定在第七个layer,以便于突出玩家处于环境中间。

并为了跳跃手感改变重力scale,这可以实现重力对不同的物体有不同的效果。

下面这段代码实现了移动和跳跃

  1. public class MyPlayerController : MonoBehaviour
  2. {
  3. new private Rigidbody2D rigidbody;
  4. private float input;
  5. private bool isGround;
  6. [SerializeField]
  7. public Vector3 checkPointOffset;
  8. [SerializeField]
  9. public LayerMask groundLayer;
  10. public int moveSpeed = 10;
  11. public int jumpStartSpeed = 10;
  12. // Start is called before the first frame update
  13. void Start()
  14. {
  15. rigidbody = GetComponent<Rigidbody2D>();
  16. }
  17. // Update is called once per frame
  18. void Update()
  19. {
  20. input = Input.GetAxisRaw("Horizontal");
  21. isGround = Physics2D.OverlapCircle(transform.position + new Vector3(checkPointOffset.x, checkPointOffset.y, 0), checkPointOffset.z, groundLayer);
  22. Move();
  23. }
  24. void Move()
  25. {
  26. rigidbody.velocity = new Vector2(input * moveSpeed, rigidbody.velocity.y);
  27. if (Input.GetKeyDown(KeyCode.W) && isGround)
  28. {
  29. rigidbody.velocity = new Vector2(0, jumpStartSpeed);
  30. }
  31. if (rigidbody.velocity.x < 0)
  32. transform.localScale = new Vector3(-1, 1, 1);
  33. else if (rigidbody.velocity.x > 0)
  34. transform.localScale = new Vector3(1, 1, 1);
  35. }
  36. }

这两个标头具有序列化的作用:

 

 

加入移动和跳跃动画

exit time打断时间(从哪一帧开始逐渐降低当前动画的权重)、

fixed duration固定持续时间(这其实是一个切换开关,切换是按照秒显示还是按照百分比显示)、transition duration过度持续时间 (过度有多长)、

勾选这个选项其实影响的是以百分比还是秒来显示:

transition offset过度抵消(从下一个动画的什么位置开始播放)、

interruption source中断来源 

先创建控制器并且拖入站立和跑步和跳跃的动画,并进行衔接:

同时删去过渡时间:

在动画控制器中 如果动画切换的条件是用一个与来表示的话 可以这样:

(注意对于速度的设置判断有时候不要判断是否为0,因为有时候即使没动,物体的速度可能也是一个非常小的值:

如果是用或条件表示的话,就多添加一次transtition,就会变成下图所示:

 此处举例,从站立到跑有两个切换条件

 

 jump的切换使用触发器:

随后即可实现跑跳

接下来加入降落的动画,即人物到达最高点后,会播放降落的动画:

三种状态都有可能进入fall状态,因此进行设置

 为了降落更加流畅,给Fall添加一个无条件的falling动画,之后动画变成这样:

之后即可实现降落。

接下来实现着陆地面,由于着陆到地面有三种前置可能:jump、fall、falling,因此都需要添加转换。并且落地后无条件变为idle,如下所示:

 让玩家着陆1秒后变为idle状态:

实现如下:

 

此时代码如下:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class MyPlayerController : MonoBehaviour
  5. {
  6. new private Rigidbody2D rigidbody;
  7. private float input;
  8. private bool isGround;
  9. [SerializeField]
  10. public Vector3 checkPointOffset;
  11. [SerializeField]
  12. public LayerMask groundLayer;
  13. public int moveSpeed = 5;
  14. public int jumpStartSpeed = 10;
  15. //----------下面这部分实现动画
  16. private Animator animator;
  17. // Start is called before the first frame update
  18. void Start()
  19. {
  20. rigidbody = GetComponent<Rigidbody2D>();
  21. animator = GetComponent<Animator>();
  22. }
  23. // Update is called once per frame
  24. void Update()
  25. {
  26. input = Input.GetAxisRaw("Horizontal");
  27. isGround = Physics2D.OverlapCircle(transform.position + new Vector3(checkPointOffset.x, checkPointOffset.y, 0), checkPointOffset.z, groundLayer);
  28. Move();
  29. }
  30. void Move()
  31. {
  32. rigidbody.velocity = new Vector2(input * moveSpeed, rigidbody.velocity.y);
  33. animator.SetFloat("Horizontal", rigidbody.velocity.x);
  34. animator.SetFloat("Vertical", rigidbody.velocity.y);
  35. animator.SetBool("isGround", isGround);
  36. if (Input.GetKeyDown(KeyCode.W) && isGround)
  37. {
  38. rigidbody.velocity = new Vector2(0, jumpStartSpeed);
  39. animator.SetTrigger("Jump");
  40. }
  41. if (rigidbody.velocity.x < 0)
  42. transform.localScale = new Vector3(-1, 1, 1);
  43. else if (rigidbody.velocity.x > 0)
  44. transform.localScale = new Vector3(1, 1, 1);
  45. }
  46. }

加入攻击动作

首先导入资源,加入三个轻攻击动画到动画机里,由于希望任何状态都可以攻击,所以用any state衔接:

设置好过度条件

并且播放完攻击动画希望玩家再次进入idle,因此可以将过度衔接到exit:

重攻击同理,随后效果如下:

接下来书写Attack函数:

  1. private int lightCombo = 1;
  2. private int heavyCombo = 1;
  3. private int comboInterval = 2;//连击的间隔,一旦超过这个间隔则玩家连击会中断
  4. private float comboTimer = 0;//连击计时器,用来记录连击到上次过了多久
  5. bool isAttack = false;
  6. void Attack()
  7. {
  8. if (!isAttack && Input.GetKeyDown(KeyCode.J))
  9. {
  10. isAttack = true;
  11. animator.SetTrigger("LightAttack");
  12. animator.SetInteger("LightCombo", lightCombo);
  13. lightCombo++;
  14. if (lightCombo > 3) lightCombo = 1;
  15. comboTimer = 0;
  16. }
  17. if (!isAttack && Input.GetKeyDown(KeyCode.K))
  18. {
  19. isAttack = true;
  20. animator.SetTrigger("HeavyAttack");
  21. animator.SetInteger("HeavyCombo", heavyCombo);
  22. heavyCombo++;
  23. if (heavyCombo > 3) heavyCombo = 1;
  24. comboTimer = 0;
  25. }
  26. comboTimer += Time.deltaTime;
  27. if (comboTimer >= comboInterval)
  28. {
  29. comboTimer = 0;
  30. lightCombo = 0;
  31. heavyCombo = 0;
  32. }
  33. }

以及一个attackOver函数,用于函数结束时调用:

  1. public void AttackOver()//用来给unity在动画结束时调用,采用帧事件的形式
  2. {
  3. isAttack = false;
  4. }

接下来添加帧事件,在动画播放完关键连击时插入关键帧,用于调用attackover函数。

通常这种关键帧不会放在动画的最后一帧,这样有助于连击的连贯性。 

但是我们发现攻击时仍可以移动,我们需要取消这点,取而代之,使用攻击时自动位移的方式实现位移补偿。

  1. private string attackType;
  2. [Header("补偿速度")]
  3. public float lightAttackSpeed=1.3f;
  4. public float heavyAttackSpeed=0.7f;
  5. void Move()
  6. {
  7. if (!isAttack) {
  8. rigidbody.velocity = new Vector2(input * moveSpeed, rigidbody.velocity.y);
  9. }
  10. else
  11. {
  12. if(attackType=="Light") rigidbody.velocity = new Vector2(transform.localScale.x * lightAttackSpeed, rigidbody.velocity.y);
  13. if (attackType == "Heavy") rigidbody.velocity = new Vector2(transform.localScale.x * heavyAttackSpeed, rigidbody.velocity.y);
  14. }
  15. }

此处代码犯了一个错,清空连击应该设置为1而不是设置为0:

此处还犯了一个bug,bug动图如下:如果在移动中按下攻击键,则玩家不会立即进入攻击状态而是跑一小段距离再进入攻击,如果把衔接的has exit time移除即可。

在实际上自己做这一步的时候一下子犯了两个bug,以后也可能会出现一下子遇见多个bug的情况

解决bug的思路其实很简单,重点观察那些参数,检查代码是否有误,无误则检查是否是动画器某个衔接设置错了

实现打击感

添加敌人受击动画与击退

我们接下来添加敌人,并为玩家的layer设置为player,敌人的layer设置为enemy,并且我们不希望player和enemy发生碰撞,因此在设置中选择:

为敌人添加动画控制器:

 

 接下来需要在攻击动画的对应时刻进行攻击范围的判定,因此需要加特定的因素然后在特定帧修改,通过录制动画的形式。

 

加好之后,接下来书写代码:

给玩家添加attackArea后,注意层级不能设置为player否则无法发生触碰。

触碰敌人的代码:

  1. private void OnTriggerEnter2D(Collider2D other)
  2. {
  3. if (other.CompareTag("Enemy"))
  4. {
  5. if (transform.localScale.x > 0) other.GetComponent<MyEnemy>().GetHit(Vector2.right);
  6. if (transform.localScale.x < 0) other.GetComponent<MyEnemy>().GetHit(Vector2.left);
  7. Debug.Log("attack success");
  8. }
  9. }

Enemy的函数:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class MyEnemy : MonoBehaviour
  5. {
  6. private new Rigidbody2D rigidbody;
  7. private bool isHit;
  8. private Animator animator;
  9. private AnimatorStateInfo animStateInfo;
  10. public float speed;
  11. private Vector2 direction;
  12. // Start is called before the first frame update
  13. void Start()
  14. {
  15. animator = GetComponent<Animator>();
  16. rigidbody = GetComponent<Rigidbody2D>();
  17. }
  18. // Update is called once per frame
  19. void Update()
  20. {
  21. animStateInfo = animator.GetCurrentAnimatorStateInfo(0);
  22. if (isHit)
  23. {
  24. rigidbody.velocity = direction * speed;
  25. if (animStateInfo.normalizedTime > 0.6f)//敌人后退的时间
  26. {
  27. isHit = false;
  28. }
  29. }
  30. }
  31. public void GetHit(Vector2 direction)//公开的给玩家调用的函数
  32. {
  33. transform.localScale = new Vector3(-direction.x, 1, 1);//与玩家的方向相反
  34. isHit = true;
  35. this.direction = direction;//让敌人按照玩家朝向的方向后退
  36. animator.SetTrigger("Hit");
  37. }
  38. }

效果如下

添加敌人受击特效

为敌人添加子物体,并为其创建帧动画,帧动画由特效组成:

然后设置动画器:

 在enemy脚本中添加:

效果如下:

 攻击时的屏幕振动

此处希望这个类作为工具类,让其他脚本方便的调用里面的函数,所以这里采用单例模式编写,可以直接调用其方法而无需实例化。

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class AttackShake : MonoBehaviour
  5. {
  6. private static AttackShake instance;
  7. public static AttackShake Instance
  8. {
  9. get
  10. {
  11. if (instance == null)
  12. instance = Transform.FindObjectOfType<AttackShake>();
  13. return instance;
  14. }
  15. }
  16. public void HitPause(int duration)
  17. {
  18. StartCoroutine(Pause(duration));
  19. }
  20. IEnumerator Pause(int duration)
  21. {
  22. float pauseTime = duration / 60f;
  23. Time.timeScale = 0;//暂时暂停时间
  24. yield return new WaitForSecondsRealtime(pauseTime);
  25. Time.timeScale = 1;
  26. }
  27. private bool isShake;
  28. public void CameraShake(float duration,float strength)
  29. {
  30. if (!isShake) StartCoroutine(Shake(duration, strength));
  31. }
  32. IEnumerator Shake(float duration,float strength)
  33. {
  34. isShake = true;
  35. Transform camera = Camera.main.transform;
  36. Vector3 startPos = camera.position;
  37. while (duration > 0)
  38. {
  39. camera.position = Random.insideUnitSphere * strength+startPos;
  40. duration -= Time.deltaTime;
  41. yield return null;
  42. }
  43. isShake = false;
  44. }
  45. }

上面实现了卡顿和抖动的函数,接下来只需要让玩家命中敌人时调用该函数并传入参数即可。

在playerController中:

  1. [Header("打击感")]
  2. public float shakeTime=0.1f;
  3. public int lightPause = 6;
  4. public float lightStrength=0.015f;
  5. public int heavyPause=12;
  6. public float heavyStrength=0.065f;
  7. private void OnTriggerEnter2D(Collider2D other)
  8. {
  9. if (other.CompareTag("Enemy"))
  10. {
  11. if (transform.localScale.x > 0) other.GetComponent<MyEnemy>().GetHit(Vector2.right);
  12. if (transform.localScale.x < 0) other.GetComponent<MyEnemy>().GetHit(Vector2.left);
  13. Debug.Log("attack success");
  14. if (attackType == "Light")
  15. {
  16. AttackShake.Instance.HitPause(lightPause);
  17. AttackShake.Instance.CameraShake(shakeTime, lightStrength);
  18. }
  19. if (attackType == "Heavy")
  20. {
  21. AttackShake.Instance.HitPause(heavyPause);
  22. AttackShake.Instance.CameraShake(shakeTime, heavyStrength);
  23. }
  24. }
  25. }

然后根据攻击类型是轻重攻击调用不同的参数即可,最终效果如下:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/90693
推荐阅读
相关标签
  

闽ICP备14008679号