Unity经典游戏教程之:合金弹头
版权声明:
- 本文原创发布于博客园"优梦创客"的博客空间(网址:
http://www.cnblogs.com/raymondking123/)以及微信公众号“优梦创客”(微信号:unitymaker) - 您可以自由转载,但必须加入完整的版权声明!
场景搭建
- 将第一关的3张背景图片收尾相接
按住V键可以选中4个角进行拖拽和拼接



- 给它们一个共同的碰撞器作为地面
- 给场景中的石头添加碰撞器
- 给木头平台添加平台效果器

预制体的制作
木桶预制体
- 设置碰撞器,刚体设置为静态
- 添加脚本和动画控制器

3. 制作动画当木桶碰到子弹则修改木桶的sprite
木桶有两滴血,当血量等于0的时候,播放闪烁的动画

敌人预制体
- 设置碰撞器和刚体
- 添加脚本和动画控制器

3. 制作动画
敌人碰到子弹后会播放死亡动画
敌人每隔5秒之后会向前移动一段距离,如果碰到玩家则会发起攻击,如果没有碰到则再过5秒之后继续移动
敌人只有出现在Camera内的时候才会移动,否则不进行移动计时
老人预制体
- 设置碰撞器、触发器和刚体
- 添加脚本和动画控制器

3. 制作动画



当老人在被绑住的时候碰到子弹,则被释放,播放释放动画当老人在被绑住的时候碰到玩家,玩家可以穿过它
当老人被释放后碰到玩家,则会给玩家一个礼物,玩家吃掉礼物后会改变自身子弹的sprite
当老人给完礼物后会穿过玩家跑走
敌机预制体
- 设置碰撞器和刚体
- 添加脚本和动画控制器

3. 制作动画
敌机在玩家走到某个位置时才会显示,并且追踪玩家的位置
当与玩家的位置小于0.5时,会向下投放炸弹,若玩家被炸弹击中则游戏结束
敌机有10滴血,若血量降到0,则播放爆炸动画,动画播放完成销毁敌机
子弹预制体
- 设置碰撞器和刚体
- 添加脚本和动画控制器

利用对象池,在游戏开始的时候就创建10个子弹,每当玩家发出子弹则改变对象池中的某个子弹的位置
当子弹位置离开玩家5个单位以上则会重置到开始位置
若子弹碰到碰撞体之后子弹会被重置
炸弹预制体
- 设置碰撞器和刚体
- 添加脚本和动画控制器

Player的制作
- 将Body和Foot进行拼接,放在一个空节点下
- 创建一个空节点作为子弹的发出位置,并作为玩家的子节点
- 在Player脚下的碰撞器之外创建两个空节点作为射线检测的开始和结束位置用于检测是否落地


4. Player动画控制器的制作

5. Player移动的脚本实现
玩家的转身是通过旋转Y轴180°实现的
通过获取按键的数值,改变对象Y轴的旋转
Y轴的速度不能为0,不然跳不起来
Y轴的在空中或地上都要判断,而X轴的移动只能在地上才能取得
只有在地上的时候才能播放walk动画
跳跃的时候要将Walk的条件置为非,否则动画会在Jump和Walk之间来回切换
在下一帧才播放shoot动画,但子弹却在这一帧出现,有点违和
解决方法:将子弹出现的位置延后 并且将渲染关掉,当子弹的X轴位置大于人物X轴位置+1时,开启渲染
当蹲下射击的时候子弹的位置为player的位置(player的位置比bulletLeftRight靠下一点,刚好满足下蹲发射降低高度的需求)
void FixedUpdate()
{
if (!GameControl.instance.isGameOver)
{
x = Input.GetAxis("Horizontal");
y = Input.GetAxis("Vertical");
absx = Mathf.Abs(x);
ground = Physics2D.Linecast(hitlinestart.position, hitlineend.position, 1 << this.gameObject.layer);
//通过获取按键的数值,改变对象Y轴的旋转,除去0 是为了让player保持之前一个方向
if (x > 0 && ground)
transform.eulerAngles = new Vector3(0, 180, 0);
else if (x < 0 && ground)
transform.eulerAngles = Vector3.zero;
playerDir = transform.eulerAngles.y == 0 ? -1 : 1;//判断人物方向有没有改变,取得子弹的方向
GetComponent<Rigidbody2D>().velocity = new Vector2(x * xspeed, GetComponent<Rigidbody2D>().velocity.y);//Y轴的速度不能为0,不然跳不起来
GetComponent<Animator>().SetFloat("DirY", y);//Y轴的在空中或地上都要判断,而X轴的移动只能在地上才能取得
if (ground)//只有在地上的时候才能播放walk动画
{
GetComponent<Animator>().SetFloat("DirX", absx);
//GetComponent<Animator>().SetFloat("DirY", y);
GetComponent<Animator>().SetBool("Jump", false);
if (Input.GetKeyDown(KeyCode.K))//按了空格且在地上
{
GetComponent<Rigidbody2D>().AddForce(new Vector2(0, yspeed));
GetComponent<Animator>().SetBool("Jump", true);
GetComponent<Animator>().SetFloat("DirX", 0);//跳跃的时候要将Walk的条件置为非,否则动画会在Jump和Walk之间来回切换
}
}
if (Input.GetKeyDown(KeyCode.J) && ground)//播放射击动画的逻辑
{
GetComponent<Animator>().SetBool("Shoot", true); //在下一帧才播放shoot动画,但子弹却在这一帧出现,有点违和
//解决方法:将子弹出现的位置延后 ,并且将渲染关掉,当子弹的X轴位置大于人物X轴位置+1时,开启渲染
bullets[bulletIndex].GetComponent<SpriteRenderer>().enabled = false;
bullets[bulletIndex].transform.position = bulletLeftRight.position; //子弹的出现位置
bullets[bulletIndex].transform.rotation = transform.rotation;//子弹的旋转跟人物的旋转一致
bullets[bulletIndex].GetComponent<Rigidbody2D>().velocity = new Vector2(playerDir * bulletSpeed, 0);//默认子弹的朝向跟人物一致,Y轴速度为0
if (Input.GetKey(KeyCode.W)) //按住上的同时按下J,向上射击
{
bullets[bulletIndex].transform.position = transform.position + Vector3.up + playerDir * Vector3.right;//修改子弹出现的位置
bullets[bulletIndex].transform.eulerAngles = new Vector3(0, 0, -90f);//旋转子弹的角度
bullets[bulletIndex].GetComponent<Rigidbody2D>().velocity = new Vector2(0, bulletSpeed);//给子弹一个向上的速度
}
else if (Input.GetKey(KeyCode.S)) //按住下的同时按下J,蹲下射击
{
bullets[bulletIndex].transform.position = transform.position;//子弹的位置为player的位置(player的位置比bulletLeftRight靠下一点,刚好满足下蹲发射降低高度的需求)
}
bulletIndex++;
bulletIndex %= bullets.Length;
}
else if (!ground && Input.GetKeyDown(KeyCode.J) && Input.GetKey(KeyCode.W))//禁止玩家在空中左右射击
{
GetComponent<Animator>().SetBool("Shoot", true); //在下一帧才播放shoot动画,但子弹却在这一帧出现,有点违和
//解决方法:将子弹出现的位置延后,并且将渲染关掉,当子弹的X轴位置大于人物X轴位置+1时,开启渲染
bullets[bulletIndex].GetComponent<SpriteRenderer>().enabled = false;
bullets[bulletIndex].transform.position = transform.position + Vector3.up + playerDir * Vector3.right;//修改子弹出现的位置
bullets[bulletIndex].transform.eulerAngles = new Vector3(0, 0, -90f);//旋转子弹的角度
bullets[bulletIndex].GetComponent<Rigidbody2D>().velocity = new Vector2(0, bulletSpeed);//给子弹一个向上的速度
bulletIndex++;
bulletIndex %= bullets.Length;
}
//将射击动画改为idel动画的条件,是子弹的位置跟人物位置距离超过5
if (bulletIndex >= 1)
{
if (Mathf.Abs(bullets[bulletIndex - 1].transform.position.x - transform.position.x) > 2)
GetComponent<Animator>().SetBool("Shoot", false);
}
else
{
if (Mathf.Abs(bullets[bullets.Length - 1].transform.position.x - transform.position.x) > 2)
GetComponent<Animator>().SetBool("Shoot", false);
}
}
}
Car的制作
- 将图片按照顺序进行拼接
- 将拼接好的车子放到一个空节点,给这个空节点一个刚体用于给它速度


GameOver和Win的显示
- 添加两个Text的UI组件,Text的内容分别为GameOver和You Win
- 在玩家死亡或者胜利的时候enable对应的Text


摄像机移动的脚本实现
- 将摄像机的X轴控制在它自身到35之内,并随着玩家位置改变(保证摄像机不能往回走)
camerax = Mathf.Clamp(player.transform.position.x, camerax > 0 ? camerax : 0, 35.5f*level);
playerx = Mathf.Clamp(player.transform.position.x, camerax - 8f, camerax + 8f);//将玩家位置控制在摄像机范围之内(保证玩家不能往回走)
player.transform.position = new Vector3(playerx, player.transform.position.y, player.transform.position.z);
mainCamera.transform.position = new Vector3(camerax, mainCamera.transform.position.y, -10);
开场动画的实现
- 通过时间来控制Car的移动和停止
- 通过MovePosition来让玩家有一个从车上跳出的效果,且在站定之后转向
private void JumpOutOfCar()//开场动画
{
if (car.position.x >= -0.01)
{
player.SetActive(true);
time += Time.fixedDeltaTime;
if (time < 0.4f)
{
player.GetComponent<Rigidbody2D>().MovePosition(Vector2.MoveTowards(player.transform.position, new Vector2(-3f, 0f), 3.5f * Time.fixedDeltaTime));//人物跳起移动
}
else if (time > 0.4f && time < 1f)
{
player.GetComponent<Rigidbody2D>().MovePosition(Vector2.MoveTowards(player.transform.position, new Vector2(-4f, -1.65f), 3.5f * Time.fixedDeltaTime));//人物下落移动
}
else if (time > 1.5f)
{
player.transform.rotation = new Quaternion(0, 180, 0, 0);//人物转向
}
}
}
敌机追踪玩家的脚本实现
- 判断玩家的位置是在Jet的左边还是右边,且向玩家位置移动
- 当与玩家位置小于0.5则扔炸弹
- 若血量为0 ,则在播放完成爆炸后销毁Jet
void FixedUpdate()
{
trax = Mathf.Clamp(transform.position.x,29f,42f);
transform.position = new Vector3(trax, transform.position.y, 0);
if (GameControl.instance.mainCamera.transform.position.x > 35)//摄像机的位置控制直升机的出现
GetComponent<SpriteRenderer>().enabled = true;
if(GetComponent<SpriteRenderer>().enabled == true&&hp>0)
{
if(transform.position.x > GameControl.instance.player.transform.position.x+0.5)
{
transform.eulerAngles = new Vector3(0, 0, 0);
GetComponent<Rigidbody2D>().velocity = 2*Vector2.left;
}
else if(transform.position.x < GameControl.instance.player.transform.position.x-0.5)
{
transform.eulerAngles = new Vector3(0, 180, 0);
GetComponent<Rigidbody2D>().velocity = 2 * Vector2.right;
}
else
{
GetComponent<Rigidbody2D>().velocity = Vector2.zero;
if(!boomAlive)
{
Instantiate(jetBullet, transform.position + Vector3.down, Quaternion.identity);
boomAlive = true;
}
}
}
else
GetComponent<Rigidbody2D>().velocity = Vector2.zero;
if (hp == 0)
{
GetComponent<Animator>().SetBool("Explosion", true);
Invoke("DestroyThis", 3f);
Invoke("SetGameWin",3f);
}
}
Older给礼物和被释放的脚本实现
- 老人被释放后如果不持续施加速度,那么速度会因为摩擦力而衰减,最后导致速度为0
- 在没被释放之前,把older的碰撞器改为触发器,使玩家可以穿过older
- 在没被释放之前,玩家可以穿过older,玩家离开碰撞器后还把该碰撞器改回去
void FixedUpdate()
{
if(isRelease)
{
time += Time.fixedDeltaTime;
if (time >= 1f && time <=6f&&!isGiving)
{
GetComponent<Animator>().SetBool("run", true);
GetComponent<Rigidbody2D>().velocity = new Vector2(-4f, GetComponent<Rigidbody2D>().velocity.y);
}
else if (time > 6)
Destroy(this.gameObject);
}
}
public void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "Bullet")
{
isRelease = true;
GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Dynamic;
GetComponent<Animator>().SetBool("release", true);
}
if (collision.gameObject.tag == "Player" && !isGift&&isRelease)
{
isGift = true;//防止多次给礼物
isGiving = true;//防止给礼物时还在移动
GetComponent<Rigidbody2D>().velocity = Vector2.zero;
GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Kinematic;
collision.gameObject.GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Kinematic;
GetComponent<Animator>().SetBool("give", true);
Invoke("GiveOver", 1f);
}
else if (collision.gameObject.tag == "Player"&& !isRelease)
{//
GetComponent<BoxCollider2D>().isTrigger = true;
}
}
public void OnTriggerExit2D(Collider2D collision)
{
if (collision.gameObject.tag == "Player")
{
if (isRelease)
{
GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Dynamic;
collision.gameObject.GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Dynamic;
}
else
{//
GetComponent<BoxCollider2D>().isTrigger = false ;
}
}
}
private void GiveOver()
{
isGiving = false;
GetComponent<Animator>().SetBool("give", false);
Instantiate(gift, transform.position + Vector3.right, Quaternion.identity);
}
Unity经典游戏教程之:合金弹头的更多相关文章
- Unity经典游戏教程之:雪人兄弟
版权声明: 本文原创发布于博客园"优梦创客"的博客空间(网址:http://www.cnblogs.com/raymondking123/)以及微信公众号"优梦创客&qu ...
- Unity经典游戏教程之:贪吃蛇
版权声明: 本文原创发布于博客园"优梦创客"的博客空间(网址:http://www.cnblogs.com/raymondking123/)以及微信公众号"优梦创客&qu ...
- Unity经典游戏教程之:是男人就下100层
版权声明: 本文原创发布于博客园"优梦创客"的博客空间(网址:http://www.cnblogs.com/raymondking123/)以及微信公众号"优梦创客&qu ...
- Unity经典游戏教程之:冒险岛
版权声明: 本文原创发布于博客园"优梦创客"的博客空间(网址:http://www.cnblogs.com/raymondking123/)以及微信公众号"优梦创客&qu ...
- Unity经典游戏教程之:弓之骑士
版权声明: 本文原创发布于博客园"优梦创客"的博客空间(网址:http://www.cnblogs.com/raymondking123/)以及微信公众号"优梦创客&qu ...
- Unity经典游戏编程之:球球大作战
版权声明: 本文原创发布于博客园"优梦创客"的博客空间(网址:http://www.cnblogs.com/raymondking123/)以及微信公众号"优梦创客&qu ...
- C#开发Unity游戏教程之Unity中方法的参数
C#开发Unity游戏教程之Unity中方法的参数 Unity的方法的参数 出现在脚本中的方法,无论是在定义的时候,还是使用的时候,后面都跟着一对括号“( )”,有意义吗?看起来最多也就是起个快速识别 ...
- C#开发Unity游戏教程之Scene视图与脚本的使用
C#开发Unity游戏教程之Scene视图与脚本的使用 Unity中Scene视图的快捷操作 Scene视图是开发者开发游戏时,操作最频繁的视图.因为一旦一个游戏对象被添加到游戏的场景中,就需要首先使 ...
- Unity实战案例教程之:不免费的PacMan(初级→中级)
课程内容介绍: 本套课程适合以下人士: - 免费资料没教会你游戏开发的: - 学了Unity基础不知道怎么用在游戏项目里的: - 想快速开发一款好玩的游戏的: - 想学游戏不知道如何入门的: - 对游 ...
随机推荐
- 恢复云数据库MySQL的备份文件到自建数据库遇到的报错
报错信息 : 恢复云数据库MySQL的备份文件到自建数据库,自建数据库版本5.6.36. 按照阿里云文档操作,启动数据库报错 -- :: [ERROR] /application/mysql/bin/ ...
- JS处理时间戳、前台拿到日期时间戳,时间戳转日期格式
今晚做分页的时候,遇到后台往前台传日期类型,会出现这种情况,好像是微软为了解决操作系统兼容性问题,将日期全部转换为从1970年1月1日至现在时间的时间戳.为了解决这个问题,特意百度了一番,整理了处理日 ...
- 关于svn服务部署方案
本文只记录我的笔记 首先, 我是个懒人, 写好了shell, 直接上传把 安装包:SvnPackages-chenglee 第一, 无非就是搞掂依赖这一块 #********************* ...
- Python之Pandas库学习(一):简介
官方文档 1. 安装Pandas windos下cmd:pip install pandas 导入pandas包:import pandas as pd 2. Series对象 带索引的一维数组 创建 ...
- ajax 的登录认证
在models中 先创建一个表 from django.db import models # Create your models here. class UserInfo(models.Model) ...
- python接口自动化(三十三)-python自动发邮件总结及实例说明番外篇——下(详解)
简介 发邮件前我们需要了解的是邮件是怎么一个形式去发送到对方手上的,通俗点来说就是你写好一封信,然后装进信封,写上地址,贴上邮票,然后就近找个邮局,把信仍进去,其他的就不关心了,只是关心时间,而电子邮 ...
- ~~函数基础(二):返回值&作用域~~
进击のpython 函数的返回值和作用域 上文我们讲到了函数的基础--参数的相关问题 举的例子也都是带有print的函数定义 但是有个问题就出现了:我不想打印这个函数处理后的参数 我想拿到这个参数然后 ...
- springcloud入门系列
关于springcloud 1.写在前面 写着写这,不知不觉springcloud写了7,8篇了,今天把文章分下类,写下感受及后面的计划吧. (1)springcloud中最最重要的是eureka注册 ...
- 深入理解 JavaScript 面向对象
我们在学习编程时,避免不了会接触一个概念,叫:面向对象编程(Object-oriented programming,缩写:oop) (不是搞对象那个对象哈),其实我们的编程方式,不止有面向对象,还有 ...
- findBugs英文代号的对照表
findBugs错误英文翻译rule.findbugs.IMSE_DONT_CATCH_IMSE.name=不良实践 - 捕获可疑IllegalMonitorStateException rule.f ...