1、需求分析

  1. 地形设计,对游戏地形进行设计比如挖坑造河等。
  2. 场景设计,种树、种草、摆放房屋建筑等。
  3. 第一人称视角实现,WASD前后左右移动,空格键进行跳跃,按左Shift进行加速。
  4. 敌人的简单制作,敌人动画设计,寻路攻击玩家等。
  5. 场景的灯光设计,Bake。
  6. 天空盒制作设计。
  7. UI设计,玩家生命力、得分、游戏胜利失败。
  8. 小地图设计。
  9. 特效制作与设计。
  10. 添加一个主场景,开始游戏和退出游戏。
  11. 添加一个加载场景,进度条的设计与制作。
  12. 添加第一关场景,搭建制作第一关场景。
  13. 添加第二关场景,搭建制作第二关场景。
  14. 添加第三关场景,搭建制作第三关场景。
  15. 最后测试发布。
  16. 解决bug并优化。

功能结构图如下:

  1. 详细设计

(1)场景搭建

第一关场景的搭建,第一关的场景搭建了一个卡通的森林,这个场景主要就是包含了一些石头、树木花草什么的。然后在添加一个星空天空盒,基本上第一关场景就是这样了。

第二关场景的搭建,第二关场景搭建了一个比第一关更加卡通好看的场景,这个场景包含了流水、小桥、房屋、树木花草、云朵、天空盒、山、绚丽的粒子特效和灯光。总结来说创造了一个“行云流水”,“小桥流水人家”,“袅袅炊烟”,的一个世外桃源之地。

第三关场景的搭建,第三关场景主要就是搭建了一个石头窟,基本上就是由石头块构成。

未完待续场景,这个主要是用来退出的场景。

开始场景搭建,这个主要用来开始游戏。

加载场景搭建,这个主要就是用来作为过渡场景。

(2)玩家控制设计

玩家控制主要实现了WASD前后左右移动,按空格键进行跳跃,按住shift进行加速,然后控制摄像机进行视角的控制,然后可以开火射击敌人。

  1. 敌人控制设计

敌人主要就是实现了走、攻击、死亡动画,通过unity寻路组件进行寻路进而找到玩家进行攻击。

  1. UI设计

UI的话,主要了三部分,第一部分就是设计了一个开始游戏的界面UI,这个比较简单,还有一个就是加载场景进度条界面,稍微东西多一点的就是游戏主要界面,这个界面主要包含玩家生命力、得分、小地图、游戏胜利、游戏失败、下一关、退出游戏等元素。

  1. 添加音效

一个游戏怎么能少音效呢,在这里主要包含了一个恐怖的背景音乐和一个射击音效。

  1. 添加特效

一个游戏怎么能没有特效呢,这里主要添加了玩家射击特效以及水火烟等特效,有了这些特效以后游戏的整体感觉就不一样了。

  1. 碰撞检测与射线检测

这里通过unity自带的碰撞盒进行碰撞检测,这个实现起来还是比较简单的,射线检测主要就是通过从摄像机发出一条射线,然后通过判断射线与那个物体进行了碰撞。

到这里大部分功能就都已经实现了,最后就需要优化测试发布了。

3、编程实现

3.1场景搭建

(格式说明:说明文字、附截图与关键操作步骤/核心代码)

  1. 开始场景搭建:

  1. 加载场景搭建:

  1. 第一关场景搭建:

  1. 第二关场景搭建:

  1. 第三关场景搭建:

  1. 未完待续场景搭建:

3.2角色控制

  1. 玩家控制

核心代码:

public class Player : MonoBehaviour{public Transform m_transform;CharacterController m_ch;float m_movSpeed = 3.0f;float m_gravity = 2.0f;public int m_life = 5;//摄像机TransformTransform m_camTransform;//摄像机旋转角度Vector3 m_camRot;//摄像机高度float m_camHeight = 1.4f;//枪口transformTransform m_muzzlepoint;//射击时可以碰撞层public LayerMask m_layer;public Transform m_fx;public AudioClip m_audio;float m_shootTimer = 0;private void Start(){m_transform = this.transform;m_ch = this.GetComponent<CharacterController>();m_camTransform = Camera.main.transform;m_camTransform.position = m_transform.TransformPoint(0, m_camHeight, 0);m_camTransform.rotation = m_transform.rotation;m_camRot = m_camTransform.eulerAngles;//Screen.lockCursor = true;Cursor.visible = false;m_muzzlepoint = GameObject.FindGameObjectWithTag("muzzlepoint").transform;}private void Update(){if(m_life<=0){return;}else{Control();}m_shootTimer -= Time.deltaTime;//射击if(Input.GetMouseButton(0) && m_shootTimer<=0){m_shootTimer = 0.1f;this.GetComponent<AudioSource>().PlayOneShot(m_audio);GameManager.Instance.SetAmmo(1);RaycastHit info;bool hit = Physics.Raycast(m_muzzlepoint.position, m_camTransform.TransformDirection(Vector3.forward), out info, 100, m_layer);if(hit){if(info.transform.tag.CompareTo("enemy")==0){Enemy enemy = info.transform.GetComponent<Enemy>();enemy.OnDamage(1);}Instantiate(m_fx, info.point, info.transform.rotation);}}}void Control(){float rh = Input.GetAxis("Mouse X");float rv = Input.GetAxis("Mouse Y");m_camRot.x -= rv;m_camRot.y += rh;m_camTransform.eulerAngles = m_camRot;Vector3 camrot = m_camTransform.eulerAngles;camrot.x = 0;camrot.z = 0;m_transform.eulerAngles = camrot;float xm = 0, ym = 0, zm = 0;ym -= m_gravity * Time.deltaTime;//控制前后左右移动if(Input.GetKey(KeyCode.W)){zm += m_movSpeed * Time.deltaTime;}else if (Input.GetKey(KeyCode.S)){zm -= m_movSpeed * Time.deltaTime;}if (Input.GetKey(KeyCode.A)){xm -= m_movSpeed * Time.deltaTime;}else if (Input.GetKey(KeyCode.D)){xm += m_movSpeed * Time.deltaTime;}m_ch.Move(m_transform.TransformDirection(new Vector3(xm, ym, zm)));m_camTransform.position = m_transform.TransformPoint(0, m_camHeight, 0);}public void OnDamage(int damage){m_life -= damage;GameManager.Instance.SetLife(m_life);if(m_life<=0){Cursor.visible = true;}}}

  1. 敌人控制

核心代码:

public class Enemy : MonoBehaviour{Transform m_transform;Animator m_ani;Player m_player;NavMeshAgent m_agent;float m_moveSpeed = 2.0f;float m_rotSpeed = 5.0f;float m_timer = 2;int m_life = 5;protected EnemySpawn m_spawn;void Start(){m_transform = this.transform;m_ani = this.GetComponent<Animator>();m_player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();m_agent = GetComponent<NavMeshAgent>();m_agent.speed = m_moveSpeed;//设置寻路目标m_agent.SetDestination(m_player.m_transform.position);}void Update(){if(m_player.m_life<=0 || !m_spawn.enabled){return;}m_timer -= Time.deltaTime;AnimatorStateInfo stateInfo = m_ani.GetCurrentAnimatorStateInfo(0);//如果处于待机且不是过渡状态if(stateInfo.fullPathHash == Animator.StringToHash("Base Layer.idle") && !m_ani.IsInTransition(0)){m_ani.SetBool("idle", false);if(m_timer>0){return;}if(Vector3.Distance(m_transform.position,m_player.m_transform.position)<1.5f){m_agent.ResetPath();m_ani.SetBool("attack", true);}else{m_timer = 1;m_agent.SetDestination(m_player.m_transform.position);m_ani.SetBool("run", true);}}//如果处于跑步且不是过渡状态if(stateInfo.fullPathHash == Animator.StringToHash("Base Layer.run") && !m_ani.IsInTransition(0)){m_ani.SetBool("run", false);if(m_timer<0){m_agent.SetDestination(m_player.transform.position);m_timer = 1;}if (Vector3.Distance(m_transform.position, m_player.transform.position) <= 1.5f){m_agent.ResetPath();m_ani.SetBool("attack", true);}}//如果处于攻击且不是过渡状态if (stateInfo.fullPathHash == Animator.StringToHash("Base Layer.attack") && !m_ani.IsInTransition(0)){RotateTo();m_ani.SetBool("attack", false);if (stateInfo.normalizedTime>=1.0f){m_ani.SetBool("idle", true);m_timer = 2;}}if (stateInfo.fullPathHash == Animator.StringToHash("Base Layer.death") && !m_ani.IsInTransition(0)){m_ani.SetBool("death", false);if (stateInfo.normalizedTime>=1.0f){GameManager.Instance.SetScore(100);m_spawn.m_enemyCount--;Destroy(this.gameObject);}}if (stateInfo.fullPathHash == Animator.StringToHash("Base Layer.attack") && !m_ani.IsInTransition(0)){RotateTo();m_ani.SetBool("attack", false);if (stateInfo.normalizedTime >= 1.0f){m_ani.SetBool("idle", true);m_timer = 2;m_player.OnDamage(1);}}}protected void RotateTo(){Vector3 targetdir = m_player.m_transform.position - m_transform.position;Vector3 newDir = Vector3.RotateTowards(transform.forward, targetdir, m_rotSpeed * Time.deltaTime, 0.0f);m_transform.rotation = Quaternion.LookRotation(newDir);}public void OnDamage(int damage){m_life -= damage;if(m_life<=0){m_ani.SetBool("death", true);m_agent.ResetPath();}}public void Init(EnemySpawn spawn){m_spawn = spawn;m_spawn.m_enemyCount++;}}

动画设计:

3.3游戏ui设计

  1. 开始界面UI

核心代码:

public class Main : MonoBehaviour{Text title;Button startButton;Button quitButton;private void Start(){GameObject uicanvas = GameObject.Find("Canvas");foreach (Transform t in uicanvas.transform.GetComponentsInChildren<Transform>()){if (t.name.CompareTo("title") == 0){title = t.GetComponent<Text>();}else if (t.name.CompareTo("StartButton") == 0){startButton = t.GetComponent<Button>();startButton.onClick.AddListener(delegate ()//按钮事件{SceneManager.LoadScene(1);});}else if (t.name.CompareTo("QuitButton") == 0){quitButton = t.GetComponent<Button>();quitButton.onClick.AddListener(delegate ()//按钮事件{Application.Quit();});}}}}

进度条:

public class ProgressBar : MonoBehaviour{int level;Text sliderText;Image bg;public Slider _progress;//过渡场景图片名称private static string LoadSceneBg= "LoadSceneBg";private static int loadSceneBgNum = 1;void Awake(){_progress = GetComponent<Slider>();level = GameManager.level;GameObject uicanvas = GameObject.Find("Canvas");foreach (Transform t in uicanvas.transform.GetComponentsInChildren<Transform>()){if (t.name.CompareTo("sliderText") == 0){sliderText = t.GetComponent<Text>();}else if(t.name.CompareTo("Bg") == 0){bg= t.GetComponent<Image>();string path = LoadSceneBg + loadSceneBgNum;bg.overrideSprite = Resources.Load(path, typeof(Sprite)) as Sprite;loadSceneBgNum++;}}}//使用协程void Start(){StartCoroutine(LoadScene());}private void Update(){//显示进度条加载数值sliderText.text = _progress.value*100 + "/100";}IEnumerator LoadScene(){//用Slider 展示的数值int disableProgress = 0;int toProgress = 0;//异步场景切换AsyncOperation op = SceneManager.LoadSceneAsync(level);//关卡数加一GameManager.level++;//不允许有场景切换功能op.allowSceneActivation = false;//op.progress 只能获取到90%,最后10%获取不到,需要自己处理while (op.progress < 0.9f){//获取真实的加载进度toProgress = (int)(op.progress * 100);while (disableProgress < toProgress){++disableProgress;_progress.value = disableProgress / 100.0f;//0.01开始yield return new WaitForEndOfFrame();}}//因为op.progress 只能获取到90%,所以后面的值不是实际的场景加载值了toProgress = 100;while (disableProgress < toProgress){++disableProgress;_progress.value = disableProgress / 100.0f;yield return new WaitForEndOfFrame();}op.allowSceneActivation = true;}}
  1. 游戏主界面

核心代码:

[AddComponentMenu("Game/GameManager")]public class GameManager : MonoBehaviour{public static int level = 2;private static string transitionToLevel= "TransitionToLevel";public static GameManager Instance = null;public int m_score = 0;public static int m_hiscore = 0;public int m_ammo = 100;Player m_player;EnemySpawn m_enemySpawn;Text txt_ammo;Text txt_hiscore;Text txt_life;Text txt_score;Text txt_gameOver;Text txt_gameVictory;Button button_restart;Button button_nextGame;Button button_quitGame;Image hud;private void OnEnable(){Time.timeScale = 1;}private void Awake(){Instance = this;m_player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();m_enemySpawn = GameObject.FindGameObjectWithTag("EnemySpawn").GetComponent<EnemySpawn>();GameObject uicanvas = GameObject.Find("Canvas");foreach (Transform t in uicanvas.transform.GetComponentsInChildren<Transform>()){if (t.name.CompareTo("txt_ammo") == 0){txt_ammo = t.GetComponent<Text>();}else if (t.name.CompareTo("txt_hiscore") == 0){txt_hiscore = t.GetComponent<Text>();txt_hiscore.text = m_hiscore.ToString();}else if (t.name.CompareTo("txt_life") == 0){txt_life = t.GetComponent<Text>();}else if (t.name.CompareTo("txt_score") == 0){txt_score = t.GetComponent<Text>();}else if (t.name.CompareTo("txt_gameOver") == 0){txt_gameOver = t.GetComponent<Text>();txt_gameOver.gameObject.SetActive(false);}else if (t.name.CompareTo("txt_gameVictory") == 0){txt_gameVictory = t.GetComponent<Text>();txt_gameVictory.gameObject.SetActive(false);}else if (t.name.CompareTo("hud") == 0){hud = t.GetComponent<Image>();}else if (t.name.CompareTo("Restart Button") == 0){button_restart = t.GetComponent<Button>();button_restart.onClick.AddListener(delegate ()//按钮事件{//Time.timeScale = 1;//读取关卡SceneManager.LoadScene(SceneManager.GetActiveScene().name);});//开始游戏按钮不可见button_restart.gameObject.SetActive(false);}else if (t.name.CompareTo("Next Button") == 0){button_nextGame = t.GetComponent<Button>();button_nextGame.onClick.AddListener(delegate ()//按钮事件{//Time.timeScale = 1;SceneManager.LoadScene(transitionToLevel);//level++;});//下一关游戏按钮不可见button_nextGame.gameObject.SetActive(false);}else if (t.name.CompareTo("Quit Button") == 0){button_quitGame = t.GetComponent<Button>();button_quitGame.onClick.AddListener(delegate ()//按钮事件{//Time.timeScale = 1;Application.Quit();});//退出游戏按钮不可见button_quitGame.gameObject.SetActive(false);}}}//更新分数public void SetScore(int score){m_score += score;if(m_score>0){m_hiscore += m_score;}txt_score.text = m_score.ToString();txt_hiscore.text = m_hiscore.ToString();}//更新弹药public void SetAmmo(int ammo){m_ammo -= ammo;if(m_ammo<=0){m_ammo = 100 - m_ammo;}txt_ammo.text = m_ammo.ToString() + "/100";}//更新生命public void SetLife(int life){txt_life.text = life.ToString();}//游戏失败public void GameOver(){if(m_player.m_life<=0){hud.gameObject.SetActive(false);txt_gameOver.gameObject.SetActive(true);button_restart.gameObject.SetActive(true);button_quitGame.gameObject.SetActive(true);m_enemySpawn.enabled = false;m_player.enabled = false;Time.timeScale = 0;Cursor.visible = true;}}//游戏胜利public void GameVictory(){if (m_player.m_life > 0 && m_score>=100){hud.gameObject.SetActive(false);txt_gameVictory.gameObject.SetActive(true);button_nextGame.gameObject.SetActive(true);button_quitGame.gameObject.SetActive(true);m_enemySpawn.enabled = false;m_player.enabled = false;Time.timeScale = 0;Cursor.visible = true;}     }private void Update(){GameOver();GameVictory();}}
  1. 结束UI

3.4添加声音

添加背景音效和射击声

3.5添加粒子特效

  1. 射击粒子特效

  1. 火焰特效

  1. 烟特效

  1. 游戏测试

发布进行测试:

  1. 开始游戏

  1. 加载场景

3、第一关胜利与失败

4、第二关

5、第三关

  1. 结束

5、总结

这次实验项目做的是一个第一人称射击游戏,整体来说功能并不是非常多,不过在比较短的时间内可以做出来这样的一个效果我也算是比较满意了,只是还有一些功能想要实现却没能实现,比如按鼠标右键进行开镜射击,把枪换为刀进行近距攻击,手榴弹投掷等。其实我也是研究了一段时间的手榴弹投掷,只是做出了了一个抛物线轨迹,其实感觉也差不多快做出来了,只需要检测手榴弹落地点,然后让手榴弹按着实现的抛物线轨迹进行运动就可以了,这个只能有空搞了。总的来说,这个项目对我来说还是帮助很大的,让我学到了很多新知识也回顾了旧知识,对于自己独立解决问题,思考问题的能力有了进一步提升,相信在未来可以学的更好。

资源包下载地址:https://download.csdn.net/download/weixin_44350205/12651631

三维游戏开发实战--狙击僵尸(unity开发)相关推荐

  1. python ai应用开发_AI应用开发实战 - 从零开始搭建macOS开发环境

    AI应用开发实战 - 从零开始搭建macOS开发环境 联系我们 OpenmindChina@microsoft.com 零.前提条件 一台能联网的电脑,使用macOS操作系统 请确保鼠标.键盘.显示器 ...

  2. 视频教程-微信小程序开发实战之番茄时钟开发-微信开发

    微信小程序开发实战之番茄时钟开发 4年web前后端开发经验,熟悉PHP,Python后端技术,熟悉基于Lnmp环境的项目开发和部署,擅长Yii,ThinkPHP,CI,Django,Flask等国内外 ...

  3. 视频教程-微信公众平台开发实战PHP版-微信开发

    微信公众平台开发实战PHP版 互联商通创始人/架构师, 在IT领域摸爬滚打20年的全栈老程序员(架构师.培训讲师).熟悉iOS.Android及LNMP的架构下快速开发互联网相关项目.精通微信和支付宝 ...

  4. 【游戏开发实战】使用Unity 2019制作仿微信小游戏飞机大战(七):主角飞机碰撞与爆炸

    文章目录 零.教程目录 一.前言 二.本篇目标 三.飞机机碰撞组件:BoxCollider2D.Rigidbody2D 四.添加Tag:Enemy 五.主角飞机碰撞处理:OnTriggerEnter2 ...

  5. 【Go API 开发实战】Go API 开发实战教程简介(1-7)

    教程介绍 现代软件架构已经逐渐从单体架构转向微服务架构,在微服务架构中服务间通信采用轻量级通信机制.对于轻量级通信的协议而言,通常基于 HTTP 和 RPC ,能让服务间的通信变的标准化并且无状态化. ...

  6. 【系】微信小程序云开发实战坚果商城-云开发开篇

    第 3-2 课:云开发开篇 目录 开篇 [系]微信小程序云开发实战坚果商城-开篇 基础篇 [系]微信小程序云开发实战坚果商城-弹性盒子 [系]微信小程序云开发实战坚果商城-ES6 简单入门 [系]微信 ...

  7. 【系】微信小程序云开发实战坚果商城-云开发之商品信息和主题商品数据实现

    第 3-8 课:云开发之商品信息和主题商品数据实现 目录 开篇 [系]微信小程序云开发实战坚果商城-开篇 基础篇 [系]微信小程序云开发实战坚果商城-弹性盒子 [系]微信小程序云开发实战坚果商城-ES ...

  8. 【系】微信小程序云开发实战坚果商城-云开发之订单品数据实现

    第 3-9 课:云开发之订单品数据实现 目录 开篇 [系]微信小程序云开发实战坚果商城-开篇 基础篇 [系]微信小程序云开发实战坚果商城-弹性盒子 [系]微信小程序云开发实战坚果商城-ES6 简单入门 ...

  9. 【系】微信小程序云开发实战坚果商城-云开发之首页数据实现

    第 3-6 课:云开发之首页数据实现 目录 开篇 [系]微信小程序云开发实战坚果商城-开篇 基础篇 [系]微信小程序云开发实战坚果商城-弹性盒子 [系]微信小程序云开发实战坚果商城-ES6 简单入门 ...

最新文章

  1. 如何查看已安装的CentOS版本信息
  2. linux 下查看log实时输出
  3. php post undefined index,PHP 中提示undefined index如何解决(多种方法)
  4. 配置classpath,引入jar包
  5. 软件质量保障之代码走查
  6. 关于知云文献翻译出现乱码
  7. html网页如何在手机上实现,html和css结合实现手机端网页自适应的代码
  8. eclipse黄色波浪线去除
  9. Arrays.sort排二维数组
  10. 提高生活、学习、工作效率的方法——时间管理Vs个人管理
  11. TB,GB,MB,KB,Byte字节,bit位 如何换算?
  12. 特斯拉Tesla Model 3整体架构解析
  13. Qlik之的应用备份(.qvf) (六)
  14. MyBatis - 批量插入
  15. 孤立森林算法 python_孤立森林(isolation forest)
  16. 查漏补缺:Java10之后,var成为关键字了吗
  17. Final %Shell高级版 激活,只需3步
  18. Java初学者入门简介
  19. Python调用百度AI接口用以实现在线语音合成 并创建简单的GUI界面 生成可执行应用程序
  20. leagcy启动模式下安装的GHOST版win7转UEFI模式

热门文章

  1. 王者荣耀服务器维护多久12.4,王者荣耀12月4日维护新活动介绍 王者荣耀维护到几点...
  2. EXCEL内容很少文件却很大---如何变小
  3. 移动端屏幕适配和css美化浏览器自带的滚动条
  4. 【QQ农场两周年】回想我的农场
  5. 67. 把字符串转换成整数
  6. nvme装系统不能自引导_怎么让老电脑实现UEFI启动NVME SSD固态硬盘进系统方法
  7. 手机端网页设计尺寸大小
  8. nginx反向代理文件下载失败
  9. laragon 更换php的版本
  10. 简单搭建一个属于自己的视频网站