目录

一、怪物的移动

二、怪物的动画

三、实现消灭动画、类的初探。


一、怪物的移动

之前我们向游戏中添加了敌人,现在要努力让敌人动起来,首先我们之前使用的是方形的碰撞器,为了防止怪物卡住我们改用圆形的。

然后我们为青蛙添加一个新的代码组件,首先和之前实现玩家的移动是一样的我们先要获得敌人的刚体,然后我们要想办法实现敌人“巡逻”的功能,我的想法是就像角色我们可以给它添加一个变量来判断是否到达地面,那么我们可以加入两个变量来担当青蛙左右巡逻的边界点,这样我在制作完成后只需要把青蛙拖入预制文件夹以后使用的时候就无需编写代码,只需要调整左右距离就可以

public Rigidbody2D rb;
public Transform leftpoint; //路径左点
public Transform rightpoint; //路径右点

定义完所有变量后在unity的界面把对应的东西拖进来

因为青蛙一开始面向左边,为了可以控制青蛙的朝向所以我们增添了一个变量(游戏角色不需要是因为可以通过输入的方向来判断)

private bool Faceleft = true;

然后我们把代码补充完整

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Enemy_Frog : MonoBehaviour
{public Rigidbody2D rb;public Transform leftpoint; //路径左点public Transform rightpoint; //路径右点public float speed; //移动速度private bool Faceleft = true;void Update(){Movement();}void Movement(){if(Faceleft) //向左移动{rb.velocity = new Vector2(-speed, rb.velocity.y);if(transform.position.x < leftpoint.position.x){transform.localScale = new Vector3(-1, 1, 1);Faceleft = false;}}else //向右移动{rb.velocity = new Vector2(speed, rb.velocity.y);if (transform.position.x > rightpoint.position.x){transform.localScale = new Vector3(1, 1, 1);Faceleft = true;}}}
}

但很快我们就发现了问题子物体随着父物体的移动会一起移动,那么我们可以选择在游戏启动后与子物体解绑或者记录下数值后销毁,也可以选择直接把两边的数值输入后,把子物体删掉了,这样的坏处是,设置的每个敌人都需要重新编写代码的巡逻边界,而且每个敌人都需要新建代码(代码不能复用)这样我们就实现了敌人移动的代码实现。

二、怪物的动画

青蛙的动画分为两种分别是站立和走动,我们现在需要给青蛙加上走动的效果(青蛙是跳起和落下)首先和之前一样把这两个动画加到青蛙的身上

加入后我们尝试连接好动画的逻辑,然后创建了用于判断状态转换的条件

然后我们重新改写代码,首先和玩家一样,只有在地面的时候才可以跳起来,我们按照碰撞体是否和地面图层相碰来识别,同理我们用这个思路修改了玩家跳跃的代码。

完整代码如下所示

    void Movement(){if(Faceleft) //向左移动{if(coll.IsTouchingLayers(ground)){rb.velocity = new Vector2(-speed, jumpforce);anim.SetBool("jumping", true);}if(transform.position.x < 2.5){transform.localScale = new Vector3(-1, 1, 1);Faceleft = false;}}else //向右移动{if (coll.IsTouchingLayers(ground)){rb.velocity = new Vector2(speed, jumpforce);anim.SetBool("jumping", true);}if (transform.position.x > 8.5){transform.localScale = new Vector3(1, 1, 1);Faceleft = true;}}       }

这里分别写了向左移动和向右移动,然后通过是否在地面来进入跳跃状态并跳跃,但此时因为青蛙不能进入下落状态。所以我们要用到新的功能:

选择idle的动画,然后添加一个事件在动画结束的地方,这样我们发现我们可以在这里调用我们写过的函数(比如movement()),这样我们可以在在每个跳跃中添入一个完整的idel状态,此时注意一定要把movement函数从idel函数中去掉,让这个函数在每次idel后自动调用

然后我们继续更改代码,我们按照游戏角色那样添加一个动画切换函数,然后在update中调用

    void SwitchAnim() //动画切换函数{if(anim.GetBool("jumping")){if(rb.velocity.y < 0.1){anim.SetBool("jumping", false);anim.SetBool("falling", true);}}if (coll.IsTouchingLayers(ground)){anim.SetBool("falling", false);}}

至此我们的敌人就实现了巡逻功能和动画切换。

三、实现消灭动画、类的初探。

首先我按照之前的过程把素材库里的其他两个敌人添加进来,一个是老鼠是比较简单的这里就不在赘述,加入各种组件和代码后就可以实现。

注意这里如果使用直接输入位置的方法来实现则一定要用角色来定位位置的数字否则就会出错

然后我们尝试加入老鹰,老鹰和前两者的不同之处在于老鹰是一个飞行的怪物,我们如果在刚体的界面设置了重力会导致它掉下来,其他地方和别的怪物并没有什么区别。

然后这里我对所有敌人的代码做了修改为了方便预制,制作更多敌人,我把直接直接输入值的方法改为了左右放两个空的子对象分别表示左右边界

然后在代码中这样书写,首先通过rightpoint和leftpoint来获取我们刚才创建的两个边界物体,然后通过right和left分别获得坐标,然后为了游戏中运行的性能我们选择直接删除这两个空对象,然后我们只需要把left和right分别写到移动函数的边界判断条件里面就可以了,这样以后我们在制作其他怪物的时候只需要拖出预制的组件然后调整left和right的位置就可以了

    public Transform rightpoint, leftpoint;private float right, left;void Start(){right = rightpoint.position.x;left = leftpoint.position.x;Destroy(rightpoint.gameObject);Destroy(leftpoint.gameObject);}

接下来是敌人消灭的动画实现,之所以把这个动画单独出来是因为,每个敌人的消灭动画都是一样的,这种情况下为每个敌人都做一个动画是比较麻烦的,所以我们用别的方法实现。

首先我尝试给青蛙添加一个消灭动画,和之前的步骤是一样的,添加过这个动画后我们需要在动画器中写一下动画逻辑(any State到死亡状态)这里面有一个不一样的就是我们把状态转移条件设为trigger(trigger的条件是没有true或者false的)

那么我们如何触发呢?但是我们现在发现一个问题,我们消灭敌人的函数是在角色的脚本中写的,我们是没有办法调用青蛙的死亡动画效果的,所以我们需要一个新方法

我们可以看到这是青蛙脚本的函数的类,如果我们生成了一个该类的对象不就可以调用该类的函数了?我们按照这个思路继续走下去。

Enemy_Frog frog = collision.gameObject.GetComponent<Enemy_Frog>();

我们在消灭角色脚本的消灭怪物的函数中加了这样一句代码,这句代码的意思是,创建了一个Enemy_Frog的对象名字叫frog,然后我们可以通过这个对象调用里面所有的组件是由函数GetComponent<Enemy_Frog>()实现的,但是为了可以销毁所以我们到frog的脚本中写一个公开的函数

    public void Death(){anim.SetTrigger("deathing");Destroy(gameObject);}

然后在玩家代码界面调用这个函数

frog.Death();

但是这样我们发现我们销毁了这个实体后死亡动画没有播放,所以我们用上面用过的动画事件,我们在死亡动画的最后一帧调用一个函数,然后我们重新写一下frog的函数

代码的逻辑是说我们会先调用Death函数,然后播放死亡动画,死亡动画结束后会调用Destory函数销毁物体

    public void Death(){anim.SetTrigger("deathing");}void Destory(){Destroy(gameObject);}

然后我发现了一个bug就是在死亡动画的播放期间,该物体还是被视作一个碰撞体依然可以触发新的死亡动画,所以我在Death里加一行代码在第一次消灭的时候就关闭碰撞体,这样避免了玩家反复跳跃的bug和死亡动画播放多次但是这样会导致一个新的bug就是没有碰撞体的物体会直接掉出地图

coll.enabled = false;

如果我们不想要这个效果就需要锁定刚体的位置,所以我们在Death()中在添加一行代码冻结它的位置

rb.constraints = RigidbodyConstraints2D.FreezePosition;

这样我们就完成了一个敌人死亡动画,那么我们以后可能会实现很多种怪物,我们每个怪物都要写一遍这个过程吗?

我们要用新的方法实现,回到代码我们可以发现每个敌人都是类MonoBehaviour的子类(类的继承)

首先我们新建一个叫Enemy的脚本,我们想要让这个脚本成为所有敌人的父类,我们把所有敌人的脚本父类修改为Enemy,然后写好Enemy的代码,关键字protected是在继承中使用的,virtual会允许子集重新编写父级的函数

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Enemy : MonoBehaviour
{protected Animator anim;protected  virtual void Start(){anim = GetComponent<Animator>();}
}

然后因为我们已经在父级中定义了Animator所以我们就不需要在子集中定义了,我们可以直接删掉,但是删除后我们原本使用的地方都不能生效了,所以为了既可以使用子集又可以使用父集我们这里给Start函数加上protect关键字,而子集需要对Start做如下的变化这样就实现了虚函数和函数的重写。

然后我们可以把死亡动画和销毁的函数搬到父类里面【注意为了可以在类外调用需要加上public的关键字】

然后发现我们搬过去后无法识别到刚体和碰撞体(因为父类没有定义)所以我们在父类初始化这些变量最后父类Enemy的代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Enemy : MonoBehaviour
{protected Animator anim;protected Rigidbody2D rb;protected Collider2D coll;protected virtual void Start(){anim = GetComponent<Animator>();rb = GetComponent<Rigidbody2D>();coll = GetComponent<Collider2D>();}public void Death(){rb.constraints = RigidbodyConstraints2D.FreezePosition;coll.enabled = false;anim.SetTrigger("deathing");}public void Destory(){Destroy(gameObject);}
}

然后我们把刚才创建角色脚本里创建对象的代码改为创建一个父类脚本,运行测试没有发现别的问题。

Enemy enemy = collision.gameObject.GetComponent<Enemy>();

其他敌人的制作过程是一样的这里不再赘述,注意这里还是要给每个敌人添加动画效果。

Unity开发日记【第七天】——怪物的移动和动画及类的实现相关推荐

  1. unity开发日记之火箭发射

    2020-06-25 夜晚,思考人生的意义,乔布斯在斯坦福的演讲说到,你的热爱就是你的事业,你的事业就是你的热爱,我想了想,什么是自己的热爱呢,这么多年,陪伴在自己身边的也就是充满乐趣的游戏吧.自己不 ...

  2. Unity开发日记【第一天】——素材的导入及地图的建立

    前言:本人由于兴趣和未来的工作原因开始尝试接触和学习Unity开发,为了避免忘记一些东西所以写下这些博客算是一个记录. 目录 一.软件的安装与配置 二.如何编辑素材以及Tilemap的使用 三.图层和 ...

  3. 【Unity】Unity开发进阶(七)双刃剑:扩展方法

    文章目录 扩展方法 扩展方法声明 如何使用 举个例子 基本原则 扩展方法的优劣分析 总结 扩展方法 C#在3.0版本中引入了"扩展方法",既有静态方法的优点,又使调用它们的代码的可 ...

  4. C# + Unity开发日记(二) 利用宏命令实现简单的代码开关

    unity提供的宏命令在开发中可以非常方便的对不同的代码进行区分,例如UNITY_EDITOR 就可以定义代码只在编辑器里运行,这样就可以在下面写一些编辑器专用的代码,而不用担心打包Android或者 ...

  5. UNITY 开发日记/教程 俄罗斯方块 (五) 方块平移和旋转

    接上文. 已经实现了方块下落之后,可以意识到,平移变得非常简单.因为我们已经实现了边界检查的函数,只要把移动目标坐标放进去判断就可以了. 在UPDATE中增加如下代码 if (Input.GetKey ...

  6. Unity开发日记【第六天】——敌人、受伤和运动手感提升

    目录 一.敌人的实现 二.受伤效果的实现 三.代码优化及二段跳实现 一.敌人的实现 今天试图将敌人添加进来让游戏变得更有趣 首先按照之前的步骤,使用素材制作三种敌人的动画 这里面需要添加的组件(敌人首 ...

  7. Unity开发日记【第八天】——音效与对话框

    目录 一.音效的实现 二.对话框的实现 一.音效的实现 声音是一个游戏必不可少的元素,现在开始尝试给游戏加入一些音效,这里面设计三个组件,分别是Audio Listener(接收).Audio Sou ...

  8. (完结)Unity游戏开发——新发教你做游戏(七):Animator控制角色动画播放

    文章目录 一.前言 二.Animator状态机组织 三.过渡条件 四.用代码设置过渡条件,触发动画播放 五.CharacterAniLogic.cs完整代码 一.前言 文章列表 Unity游戏开发-- ...

  9. [Unity]《太空射击》开发日记Ep.2(初级篇)

    文章目录 前言 敌人1脚本 预制件 敌人1的完善工作 触发器式碰撞 屏幕边缘检测 计算敌人1的飞行角度 优化敌人生成器 前言 [Unity]<太空射击>开发日记Ep.1(入门篇) 在上一篇 ...

最新文章

  1. 我想和你一起去这样一个地方
  2. activity 点击后传递数据给fragment_Fragment 新特性 : Fragment Result API 使用以及源码分析
  3. PHP之MVC项目实战(二)
  4. 关于iOS7里的JavaScriptCore framework
  5. django分页功能 views与templates
  6. win10电脑插耳机没声音_Win10如何录制电脑内部声音
  7. 获取变量数据类型(JS)
  8. 用python画圣诞树-python圣诞树
  9. 通过xmlhttp实现-报告归档
  10. [剑指Offer] 26.二叉搜索树与双向链表
  11. setInterval.js
  12. 表格元素的完全指南(译)
  13. c语言jj斗地主源码,GitHub - choushane/cocoscreator-ddz: cocos-斗地主,客户端和服务端(node.js socket.io)...
  14. 三维激光雷达点云拼接
  15. 行为识别 - TAM: Temporal Adaptive Module for Video Recognition
  16. java 无符号右移_java中的无符号移位运算
  17. Ubuntu 安装rpm文件
  18. 计算机网络基础技术:数据通信技术基本概念
  19. 从记账软件看工具类APP的存量运营之道
  20. HTTP状态码及说明大全

热门文章

  1. Selenium 键盘事件 操作大全
  2. for example: not eligible for auto-proxying
  3. synchronized,ReentrantLock、ReentrantReadWriteLock和StampedLock
  4. 《秘密》卷一:秘密-吸引力法则
  5. 电脑频繁蓝屏与AMD Ryzen5 3600超频
  6. Start Developing Mac Apps -- 开始开发Mac应用
  7. 洛谷 P5459 [BJOI2016]回转寿司 【CDQ分治】
  8. Android四大组件之Activity(第一篇-简单使用)
  9. 代码回滚,你真的理解吗?
  10. 无聊 发个金山词霸,和QQ2013 SP6去除广告方式