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基础不知道怎么用在游戏项目里的: - 想快速开发一款好玩的游戏的: - 想学游戏不知道如何入门的: - 对游 ...
随机推荐
- Python笔记【2】_列表学习
#!/usr/bin/env/python #-*-coding:utf-8-*- #Author:LingChongShi #查看源码Ctrl+左键 #字符串:通常有单引号“'”.双引号“" ...
- Presto 0.22.0 安装记录
1. 下载 & 解压 # 下载 wget https://repo1.maven.org/maven2/com/facebook/presto/presto-server/0.220/pres ...
- Codeforces 781A:Andryusha and Colored Balloons(DFS染色)
http://codeforces.com/contest/782/problem/C 题意:给一棵树染最少的颜色,使得相邻距离为2的点都是不同的颜色,问最少是多少种颜色并输出每个点的颜色. 思路:比 ...
- c++ 归并排序
c++ 归并排序 输入输出格式 输入格式: 第11行为一个正整数NN,第22行包含NN个空格隔开的正整数a_ia i ,为你需要进行排序的数,数据保证了A_iA i 不超过1000000000 ...
- 关于ffmpeg /iis 8.5 服务器下,视频截取第一帧参数配置
ffmpeg 视频截取第一帧参数配置: 网站找了很多资料,但是都不能满足要求,然后自己写下解决过程. 首先看自己PHP 版本,安全选项里面 php5.4 跟php5.6 是不一样的.去除里面的sys ...
- 动态代理模拟实现aop
AOP实现起来代码相当简单.主要核心是动态代理和反射. 一.接口类: public interface MethodDao { public void sayHello(); } 二.接口实现类: p ...
- NetCore 中间件获取请求报文和返回报文
using System; using System.IO; namespace WebApi.Restful.Middlewares { public class MemoryWrappedHttp ...
- 快速掌握mongoDB(二)——聚合管道和MapReduce
上一节简单介绍了一下mongoDB的增删改查操作,这一节将介绍其聚合操作.我们在使用mysql.sqlserver时经常会用到一些聚合函数,如sum/avg/max/min/count等,mongoD ...
- MyBatis bind标签的用法
From<MyBatis从入门到精通> <!-- 4.5 bind用法 bind标签可以使用OGNL表达式创建一个变量并将其绑定到上下文中. 需求: concat函数连接字符串,在M ...
- Gin 框架 - 使用 logrus 进行日志记录
目录 概述 日志格式 Logrus 使用 推荐阅读 概述 上篇文章分享了 Gin 框架的路由配置,这篇文章分享日志记录. 查了很多资料,Go 的日志记录用的最多的还是 github.com/sirup ...