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基础不知道怎么用在游戏项目里的: - 想快速开发一款好玩的游戏的: - 想学游戏不知道如何入门的: - 对游 ...
随机推荐
- 深入理解Java内存模型JMM与volatile关键字
深入理解Java内存模型JMM与volatile关键字 多核并发缓存架构 Java内存模型 Java线程内存模型跟CPU缓存模型类似,是基于CPU缓存模型来建立的,Java线程内存模型是标准化的,屏蔽 ...
- mybatis的插入与批量插入的返回ID的原理
目录 背景 底层调用方法 单个对象插入 列表批量插入 完成 背景 最近正在整理之前基于mybatis的半ORM框架.原本的框架底层类ORM操作是通过StringBuilder的append拼接的,这次 ...
- django基础知识之定义模型:
定义模型 在模型中定义属性,会生成表中的字段 django根据属性的类型确定以下信息: 当前选择的数据库支持字段的类型 渲染管理表单时使用的默认html控件 在管理站点最低限度的验证 django会为 ...
- 研究Electron主进程、渲染进程、webview之间的通讯
背景 由于某个Electron应用,需要主进程.渲染进程.webview之间能够互相通讯. 不过因为Electron仅提供了主进程与渲染进程的通讯,没有渲染进程之间或渲染进程与webview之间通讯的 ...
- kuangbin专题 专题一 简单搜索 Find a way HDU - 2612
题目链接:https://vjudge.net/problem/HDU-2612 题意:‘@’表示KTV,‘#’表示无法走的地方,‘Y’,'M’表示两个人,他们要在KTV见面,问他们都到达KTV要花费 ...
- c++学习书籍推荐《超越C++标准库:Boost库导论》下载
<超越C++标准库Boost库导论>不仅介绍了Boost库的功能.使用方法及注意事项,而且还深入讨论了Boost库的设计理念.解决问题的思想和技巧以及待处理的问题.因此,本书是一本了解Bo ...
- Windows下通过CMD命令行程序操作MySQL数据库
注意:如果您的MySQL没有安装在C盘下,先使用命令进入MySQL的安装目录下的bin目录中才可以进行后续操作. 方法如下:例如您安装在D盘.先输入 D: 回车即可进入D盘,再输入cd D:\您my ...
- Atom常用插件、快键键、使用技巧
atom 中间有一条白色的虚线,怎么去掉? Settings/Packages/wrap-guide disable 自动换行 File-Settings-Editor-Soft Wrap(打勾即可) ...
- 嗨,你真的懂this吗?
this关键字是JavaScript中最复杂的机制之一,是一个特别的关键字,被自动定义在所有函数的作用域中,但是相信很多JvaScript开发者并不是非常清楚它究竟指向的是什么.听说你很懂this,是 ...
- JAVA跳出指定For循环
1. 问题描述 当有多个for循环的时候,如何跳出最外层循环? 2. 解决方案 2.1 正常单个for循环 package com.example.demo; public class TestFor ...