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基础不知道怎么用在游戏项目里的: - 想快速开发一款好玩的游戏的: - 想学游戏不知道如何入门的: - 对游 ...
随机推荐
- resolv.conf 的超时(timeout)与重试(attempts)机制
/etc/resolv.conf 有两个默认的值至关重要,一个是超时的 timeout,一个是重试的 attempts,默认情况下,前者是 5s 后者是 2 次.这个估计很多工程师都不是很在意,一般情 ...
- code forces 1173 B. Nauuo and Chess
本文链接:https://www.cnblogs.com/blowhail/p/10991237.html B. Nauuo and Chess 原题链接:http://codeforces.com ...
- [转]sublime text3在指定浏览器上本地服务器(localhost)运行文件(php)
昨天在使用sublime text3时,发现能在本地服务器上运行php文件,于是百度了一下有关知识, 终于成功了,今天总结一下. 首先要让sublime text3 出现侧边栏sidebar,不会的可 ...
- HashMap源码分析(一):JDK源码分析系列
正文开始 注:JDK版本为1.8 HashMap1.8和1.8之前的源码差别很大 目录 简介 数据结构 类结构 属性 构造方法 增加 删除 修改 总结 1.HashMap简介 HashMap基于哈希表 ...
- jquery 动态 新增 元素 绑定事件
在jquery1.7以前使用live方法进行绑定,由于年代久远,这里就不介绍了. 在jquery1.7以后我们使用的on方法,这里需要注意的一点就是:如果想让动态生成的元素,都绑上事件,on前面的选择 ...
- c++ 求int数组的长度
c++ 求int数组的长度 网上有一些方法是 sizeof(arr) / sizeof(arr[0]); 这种方法放在函数中,是不对的 我自己的方法是 #include <bits/stdc++ ...
- Windows 10打开远程桌面的方法
今天使用windows 10,想要用远程桌面连接,可是怎么都找不到,哎,win10相比于win7和XP系统,感觉还是有点使用不习惯.不过后来还是找到了两个方法,在此记录下来,分享给需要的朋友. 1. ...
- MyBatis从入门到精通:update用法、delete用法
update用法: 1.接口类中添加的方法: int updateById(SysUser sysUser); 2.映射文件中添加的代码: <update id="updateById ...
- wpf怎么绑定多个值,多个控件
最近有不少wpf新手问wpf的命令怎么绑定多个控件,很多人为此绞尽脑汁,网上的答案找了也没找到靠谱的,其实用MultiBinding就可以了.从.net 3.0版本开始,就支持MultiBinding ...
- 开源一个好用的nodejs访问mysql类库
一.背景问题 自nodejs诞生以来出现了一大批的web框架如express koa2 egg等等,前端可以不再依赖后端可以自己控制服务端的逻辑.原来的后端开发同学的阵地前端如今同样也写的风生水起,撸 ...