敌人模块


策划

需求分析

EnemyMotor.cs

EnemyMotor.cs
注意:
1.transform.LookAt(targetPos) 表示当该物体设置了Lookat并指定了目标物体时,该物体的z轴将始终指向目标物体,所以MovementForward() 函数只用改变Z坐标即可,而且此处使用Time.deltaTime,是由于MovementForward()会在Pathfinding()中调用,而Pathfinding()将会在EnemyAI中的Update中调用,这使得敌人运动可以保证是恒速。
2.由于前期没有写WayLIne类,所以需要测试代码,测试子功能是否正确,注释掉部分是测试该功能的代码。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 敌人马达 提供移动、旋转、寻路的功能
/// </summary>
public class EnemyMotor : MonoBehaviour
{//临时创建路点 用于测试//public Transform[] points;//创建线路public WayLine line;//速度public float Speed = 5;//移动public void MovementForward(){transform.Translate(0, 0, Speed * Time.deltaTime);}//朝着路点旋转public void LookRotation(Vector3 targetPos){transform.LookAt(targetPos);}private int currentIndex;public bool Pathfinding(){//寻路结束if (currentIndex >= line.Points.Length || line.Points == null)return false;LookRotation(line.Points[currentIndex]);MovementForward();//到达路点后,索引值++if (Vector3.Distance(this.transform.position, line.Points[currentIndex]) <= 0.1f)currentIndex++;//临时创建路点 用于测试//if (currentIndex >= points.Length || points == null)//    return false;//LookRotation(points[currentIndex].position);//MovementForward();//if(Vector3.Distance(this.transform.position, points[currentIndex].position) <= 0.1f)//currentIndex++;return true;}}

EnemyStatusInfo.cs

EnemyStatusInfo
注意:
public EnemySpawn spawn; 是为了EnemySpwn类中的CreatEnemy()函数中产生一个敌人后,将生成该敌人的生成器赋给该spawn变量,便于在EnemyStatusInfo类中Death()函数中完成一个敌人死后,可以用该生成器生成下一个敌人,避免使用到其他生成器,造成同一路线生成多个敌人。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
// 敌人的状态信息
/// </summary>
public class EnemyStatusInfo : MonoBehaviour
{public EnemySpawn spawn;//当前血量public float currentHP = 80;private void Demage(float amount){currentHP -= amount;if (currentHP <= 0)//死亡Death();}private EnemyAnimation anim;//死亡延迟时间public float deathDelay = 5;private void Death(){//销毁当前游戏对象Destroy(gameObject, deathDelay);//播放死亡动画anim = GetComponent<EnemyAnimation>();anim.Play(anim.deatName);//生成下一个敌人spawn.GenerateEnemy();}
}

EnemyAnimation.cs

EnemyAnimation
注意:
1.定义各种动画名称的变量的原因
EnemyAnmation脚本挂在Solier物体上,而其子物体Model上有Animation脚本,由于不同Model的Animation中动作的名称不同,所以我们要定义各种动画名称的变量,且是Public的,这样就可以对照着不同Model的动画名称在界面中填入相对应的名称,这样的话就算Soldier的子物体Model修改了,我们也只用在EnemyAnmation脚本的界面中做出相应的修改即可,不用改变EnemyAnmation代码内容。
2. anim = GetComponentInChildren< Animation>(); 因为EnemyAnmation脚本挂在父物体Solier物体上,而我们需要寻找子物体Model上的Animation组件,所以此处使用GetComponentInChildren。
3. Play和CrossFade的区别:
Play:直接切换动画,如果人物之前处于倾斜跑步状态,则会立即变成站立状态,表现上比较不真实,特别是当两个动画姿势差别较大时。
CrossFade:通过动画融合来切换动画,第二个参数可以指定融合的时间,如果人物之前处于倾斜跑步状态,则会在指定的融合时间内逐渐变成站立状态,表现上接近真实的人物动作切换效果。

EnemyAnimation

using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 敌人动画
/// </summary>
public class EnemyAnimation : MonoBehaviour
{public string runName = "run";//攻击动画public string atkName = "shooting";//死亡动画public string deatName = "death";//闲置动画public string idleName = "idleWgun";private Animation anim;private void Awake(){anim = GetComponentInChildren<Animation>();}//播放动画public void Play(string name){anim.CrossFade(name);    }//判断动画是否播放public bool IsPlaying(string name){return anim.IsPlaying(name);}}

EnemyAI.cs

EnemyAI
1.快捷键:选中内容,按control+R+M键,创建新方法。
2.Update中执行游戏逻辑,根据敌人状态,执行寻路或攻击。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 敌人 人工智能
/// </summary>
public class EnemyAI : MonoBehaviour
{//获取[敌人马达、敌人动画]脚本对象引用private EnemyMotor motor;private EnemyAnimation anim;//枚举敌人状态public enum State{//攻击Attack,//寻路Pathfinding}private void Start(){motor = GetComponent<EnemyMotor>();anim = GetComponent<EnemyAnimation>();}private State state = State.Pathfinding;private float atkTimer;//攻击间隔public float atkInterval = 0.5f;//Update中执行的是游戏逻辑private void Update(){switch (state){case State.Attack:Attack();break;case State.Pathfinding:Pathfinding();break;default:break;}}private void Attack(){//当攻击动画未播放时,播放闲置动画if (!anim.IsPlaying(anim.atkName)){anim.Play(anim.idleName);}if (atkTimer <= Time.time){anim.Play(anim.atkName);atkTimer = Time.time + atkInterval;}}private void Pathfinding(){//执行寻路//如果寻路结束,修改状态为攻击anim.Play(anim.runName);if (!motor.Pathfinding())state = State.Attack;}}

敌人生成器

策划

需求分析

创建根路线

创建根路线:
1.调试阶段让路点坐标的标签显示出来,方便观察。
2.敌人生成器EnemySpawn脚本应该挂在WayLineRoot这个父物体身上,这样就可以选择下面不同的子路线来创建不同的敌人。

WayLine.cs

WayLine
1.Points这个数组存放的是三维向量的引用,所以默认是null,该Point变量的属性是可读可写。
2.当创建一条线路时,需要初始化线路,所以创建了构造函数。
3.当创建的数组内部存放的是引用类型的时候,我们往往需要New 引用类型来初始化。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 路线类
/// </summary>
public class WayLine
{//坐标数组 装的是三维向量的引用 默认是null public Vector3[] Points { get; set; }//线路是否可用 默认是 falsepublic bool IsUseAble { get; set; }//构造函数,初始化属性public WayLine(int countPoints){this.Points = new Vector3[countPoints];this.IsUseAble = true;}}

EnemySpawn

代码实现


注意事项

EnemySpawn
重点:1.lines这数组用于储存所有的路线,也是引用类型,所以需要New 引用类型来初始化,New完以后lines成为一个有数组大小的数组,且lines,IsUseable=true,但是每个数组内位置仍然为null,接下来就是遍历子物体,即界面中WayLIneRoot下面的子路线Wayline01、Wayline02、Wayline03,由于每条子路线为null,所以将每条子路线都New 引用类型,这样就初始化了每条子路线,但现在每条子路线中的Points是(0,0,0),所以遍历每条子路线的路点,将每个路点都赋值。
2.由于有的线路如果当前产生了敌人,有敌人占据,则该线路不可用 IsUseable = false,所以需要SelectWayLines()方法将所有的线路进行筛选,选出所有的有用路线,且将该路线放在List<>集合里面,由于该方法的返回值是WayLine[]数组,所以需要ToArray操作,将集合转为数组。
3.在所选择路线的起始点创建一个敌人以后,需要注意需要配置敌人的信息,这个敌人创建好以后,上面应该自动挂着EnemyAI、EnemyAnimation、EnemyStatusInfo、EnemyMotor脚本(由于创建预制体时候已经设置好了哦),由于寻路需要知道线路,所以找到所创建的敌人的EnemyMotorjiao组件的引用,将路线line传给它,此外该线路被这个敌人占据,所以当前不可用。
4.由于后面敌人可能会死亡,所以每次创建敌人时,将产生该敌人的生成器保留,为了下次可能会使用该生成器来创建这条线路的敌人,避免产生一条线路产生多个敌人。

EnemySpawn.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 敌人生成器
/// </summary>
public class EnemySpawn : MonoBehaviour
{//存储所有路线private WayLine[] lines;//记录敌人预制体public GameObject[] enemyTypes;//开始需要创建的敌人数目public int startCount = 2;//记录已经产生的敌人数量private int spawnedCount;//记录产生敌人数量的上限public int maxCount;//最大延迟时间public float maxDelay = 10;//计算所有路线以及坐标private void CalculateWayLines(){lines = new WayLine[transform.childCount];for (int i = 0; i < transform.childCount; i++){Transform WaylinesTF = transform.GetChild(i);lines[i] = new WayLine(WaylinesTF.childCount);for (int pointsIndex = 0; pointsIndex < WaylinesTF.childCount; pointsIndex++){lines[i].Points[pointsIndex] = WaylinesTF.GetChild(pointsIndex).position;}}}//产生敌人private void CreatEnemy(){//获取所有有用路线WayLine[] useableLines = SelectWayLines();//随机选择其中一条路线WayLine wayLine = useableLines[Random.Range(0, useableLines.Length)];//创建一个敌人int enemyTypeIndex = Random.Range(0, enemyTypes.Length);GameObject enemyGO = Instantiate(enemyTypes[enemyTypeIndex], wayLine.Points[0], Quaternion.identity) as GameObject;//给敌人配置信息EnemyMotor motor = enemyGO.GetComponent<EnemyMotor>();motor.line = wayLine;//wayLine.IsUseAble = false;//产生了敌人,该路线已经被占用motor.line.IsUseAble = false;enemyGO.GetComponent<EnemyStatusInfo>().spawn = this;}//计算出所有的路线,并且产生初敌人 private void Start(){CalculateWayLines();for (int i = 0; i < startCount; i++){ GenerateEnemy();}}public void GenerateEnemy(){spawnedCount++;if (spawnedCount >= maxCount) return;float delay = Random.Range(0, maxDelay);//延迟产生敌人Invoke("CreatEnemy", delay);}//获取所有有用路线private WayLine[] SelectWayLines(){List<WayLine> WayLines = new List<WayLine>(lines.Length);foreach (var item in lines){if (item.IsUseAble) WayLines.Add(item);}return WayLines.ToArray();}
}

敌人模块代码的逻辑顺序:
EnemySpawn(挂在WayLineRoot产生敌人) --> WayLine --> EnemyAI --> EnemyMotor --> EnemyAnimation --> EnemStausInfo(如果敌人受伤或死亡)

注意代码的执行顺序:由于Sodiler物体上挂有四个脚本,需要注意在同一物体上脚本的执行,关注下Awake、Start、Update的执行顺序。

武器模块

枪口火光特效



注意:
1.Gun应该放在FistPersonCharacter下面,而不是FPSController下面,因为Camera组件在FistPersonCharacter里面,这样Gun才会随着视角移动旋转,
2.因为可能有多把枪,所以创建空物体Gun,再做一个空物体HandGun作为手枪分类,该处手枪预制体放在HangGun下面。

Unity游戏实战项目-英雄无敌相关推荐

  1. 一步一步教你用 Python 开发战棋游戏,仿英雄无敌

    英雄无敌,你从没有玩过的"船"新版本-- 制作人:你自己-- 相信很多人都是从游戏开始接触电脑和编程的.很多人打算学习编程的时候,也都是冲着开发游戏去的,尤其是学习 Python ...

  2. unity游戏开发之[英雄联盟]的美女设计师:Katie De Sousa

    报道/此前SuperData的数据透露,<英雄联盟>2014年的收入将突破11亿美元,而作为近几年红遍全球的MOBA游戏,<英雄联盟>不仅推动了电子竞技的快速发展,还带动了多个 ...

  3. 我的第一个unity游戏FPS项目任务制作经历(以及个人感慨)

    1,大一当混子一整天玩游戏的混子生活 这学期我很庆幸有一个学长带我们,我比较喜欢玩游戏,所以我进行了协会软件组(大一特别混当时没有确定方向--因为我真不知道学啥根本没人告诉你,而且我也没啥电脑经验,进 ...

  4. 浅谈erlang游戏服务器项目--英雄远征服务启动流程

    1.看代码结构我们知道启动脚本run.sh,看脚本中的-s sd server_start我们知道启动接口在sd.erl的server_start函数. 2.我们打开sd.erl找到server_st ...

  5. Unity 3D游戏开发项目《战斗吧!勇士》

    目录 版权声明:本博客涉及的内容是对本人游戏作品<战斗吧!勇士>项目的总结,发布在网络上,旨在大家交流学习.互相促进.严禁用于其他一切用途. 摘要 游戏开发技术概述 Unity 3D 游戏 ...

  6. Unity游戏开发:3D冒险游戏项目实战

    具有强扩展性的3D冒险游戏开发实战 冒险游戏AVG是电子游戏的一种,它集中于探索未知.解决谜题等情节化和探索性的互动,强调故事线索的发掘,主要考验玩家的观察力和分析能力.在公司里,这种量级的游戏逻辑开 ...

  7. Android游戏开发项目实战

    Android游戏开发项目实战: 手机游戏开发工程师培训教程(Android4.3.Cocos2d-x.Untity2D/3D.跨平台引擎技术) 课程讲师:厉风行 课程分类:游戏开发 适合人群:初级 ...

  8. unity实现幸运大转盘(实战项目)

    转盘抽奖模块在游戏日常开发中经常用到,本次就为大家分享一个实战项目中的转盘实现方式(本转盘为转盘动,中间指针不动,不过指针动也是和此demo大同小异.demo下载在文章末尾,欢迎大家下载使用). 转盘 ...

  9. 三国志、英雄无敌玩腻了?没关系,我教你开发个战旗游戏玩玩

    喜欢回合制战棋游戏的玩家,肯定对<三国志曹操传>和<英雄无敌>这两款经典战旗游戏不陌生吧. 在<三国志曹操传>中,镇压黄巾军.群雄讨伐董卓.灭吕布等历史事件与游戏中 ...

最新文章

  1. UVa 10148 - Advertisement
  2. mnn python例子
  3. 微调Faster-R-CNN-InceptionV2完成高准确率安全帽检测任务
  4. rep movsd + rep movsb 内联实现 strcpy
  5. JAVA8 Optional新特性和使用详解
  6. 有一个包含主函数的java类_重新认识Java中的程序入口即主函数各组成部分
  7. ASP.NET Core SignalR中的流式传输
  8. 前端学习(392):京东制作页面1京东项目项目介绍
  9. 非阻IO与EWOULDBLOCK EAGAIN
  10. 关于javascript dom扩展:Selector API
  11. 利润从‮而何‬来?​‎
  12. ubuntu的无比注意事项
  13. 广东自考计算机专业在校生,2020年4月广东自考计算机及应用专业计划及课程设置(B080702本科)...
  14. 四两拨千斤的工作小工具分享
  15. DFT泄漏 频谱泄漏 (FFT)
  16. 数据结构上机实验1——线性表——班级管理系统
  17. 登录公司邮箱服务器怎么选,企业邮箱怎么选
  18. 高中数学基础-2.2.1 对数及对数的运算(下)
  19. 文化馆建筑方案设计原理及方案
  20. 2015年腾讯阿里实习生招聘面试经历

热门文章

  1. for(auto i : v)遍历容器元素
  2. 节日祝福 html,节日祝福语大全
  3. Python中定义函数的关键字是什么?一起来学习下吧
  4. 超松弛迭代法(SOR)的Python实现
  5. 【Qt作业】利用帧动画实现运动的小人
  6. ts文件编译与运行,vscode自动编译
  7. 上海迪士尼盛大开幕 高德地图带你玩转迪士尼
  8. 【报表开发】:BI---新视界---请休假个人报表
  9. python制作闯关答题软件_闯关答题-可以用做问答互动的软件-闯关答题会议 微信 问答游戏GO互动智能现场...
  10. 机器学习精髓-机器学习百页书