FC经典游戏还原之:松鼠大作战2
版权声明:
- 本文原创发布于博客园"优梦创客"的博客空间(网址:
http://www.cnblogs.com/raymondking123/
)以及微信公众号“优梦创客”(微信号:unitymaker) - 您可以自由转载,但必须加入完整的版权声明!
松鼠大作战游戏制作
游戏介绍 1990年,经迪士尼授权,由日本卡普空(Capcom)电视游戏公司制作的基于任天堂FC主机的电视游戏《松鼠大作战》出版发行。游戏延用迪士尼动画片《松鼠大作战》里的两只可爱花栗鼠Chip and Dale(奇奇和蒂蒂),从寻找小猫咪的委托任务开始,到摆脱肥猫陷阱与其一决高下为故事内容。可单人玩又可双人对战,可互助又可互攻,流畅度,创造力与可玩性均为同类游戏中的领军者。
1993年,卡普空制作发行了《松鼠大作战2》再一次将经典搬到屏幕。除了制作更加精良外,故事也更加惊险刺激。
场景搭建 将场景中的图片首尾拼接,在合适的位置设置碰撞器与平台控制器
主角Chip登场 将主角图片拖入层级面板上,命名为“player”,添加刚体2D组件和合适的碰撞器组件。为player设置子节点hand,添加碰撞器组件设置为触发,用于触碰箱子的判断;设置子节点foot,调整恰当的位置,用于跳跃,以及主角跳跃动画的条件判定;再设置两个节点用于主角投掷物品发射的位置。
主角Chip动画制作 打开动画控制器,创建主角的Idel动画,在合适的时间轴拖上相应的主角图片,重复操作将主角的动画设置好。
主角Chip基本动作实现 为player添加脚本组件,命名为PlayerController,主角的移动需要通过输入设备为其提供指令,并进行相应的操作,Unity中Input可以获得这些操作。horizontal与vertical均是浮点数范围为-1至1,方向与卡迪尔坐标相同,isPressjump与isPressFire均为Bool类型。通过射线的方式我们可以判断处人物是否在跳跃状态下。Chip的移动与跳跃我们通过物理运动进行操作,在FixedUpdate()下进行代码操作;
isJump = !(Physics2D.Linecast(transform.position, foot.position, 1 << LayerMask.NameToLayer("Map")) || Physics2D.Linecast(transform.position, foot.position, 1 << LayerMask.NameToLayer("Box")));
horizontal = Input.GetAxis("Horizontal");//上下
vertical = Input.GetAxis("Vertical");//左右
isPressjump = Input.GetButtonDown("Jump");//跳跃
isPressFire = Input.GetButtonDown("Fire1");//攻击
void FixedUpdate()
{
if (isControl)
{
if (!isDown && isPressjump && !isJump)
{
if (rig.velocity.y < 1F)
{
rig.AddForce(Vector3.up * force);
}
isJump = true;
}
if (isDown)
{
rig.velocity = new Vector3(0, rig.velocity.y);
}
else
{
rig.velocity = new Vector3(speed * horizontal, rig.velocity.y);
}
}
}
- 主角Chip攻击实现 与射击游戏的区别,在本作中,CHip是通过搬物品再投掷出去进行攻击的。这里的设想是将场景的箱子与投掷的箱子区别开来,方便判断。player下的节点hand添加脚本,在OnTriggerStay2D下调用MoveBox方法,isHandBox判断手中是否有箱子,isThrow则判断有没有进行投掷操作。这里贴出主要代码:
public void MoveBox(Collider2D collision)
{
//搬箱子
if (!isDown && collision.tag.StartsWith("Box") && isPressFire && !isHandBox && !isThrow)
{
Destroy(collision.gameObject);//场景中的箱子被销毁了,游戏中松鼠头顶上的箱子仅仅是外观的区别
isHandBox = true;
animator.SetTrigger("moveBox");
Instantiate(movebox);
}
}
private void ThrowBox()
{
if (isHandBox && isPressFire)
{
isHandBox = false;
isThrow = true;
GameObject o = Instantiate(throwingbox);//投掷箱子时实例化一个投掷的箱子(与场景的箱子功能不同)
if (!isDown)
{
o.transform.position = transform.Find("fireposition").position;
}
else
{
o.transform.position = transform.Find("downfireposition").position;
}
o.GetComponent<ThrowingBox>().Throw(transform.localScale.x, vertical);
StartCoroutine(ResetIsThrow());
animator.SetTrigger("throwbox");
Instantiate(throwbox);
}
}
主角Chip动画控制器动画控制 给出合适的条件进行动画转换,本作动作丰富,通过多次尝试,本作动作才比较连贯。
场景箱子的制作 将箱子图片拖进层级面板中,为它设置合适的碰撞器,做成预制体备用。
投掷箱子的制作 与场景箱子类似,设置触发器,另外需要增加刚体2D组件与脚本,脚本主要用于处理与怪物接触的交互,同样也制成预制体。
其他道具的制作 其他道具有花(吃了一定的数量可以奖励生命);蜂蜜(蜜蜂怪物攻击的道具,玩家触碰会造成伤害);坚果(若主角受伤,吃到该道具会增加1点Hp值);叉子(投掷小鼠投掷的道具,玩家触碰会造成1点Hp伤害)。
一些其他的制定 在本场景的末尾添加添加合适的触发器,绑上脚本可以通过下一个场景;在悬崖处也添加一个长条的触发器,用于触发主角死亡判定并重新加载本场景。
蜜蜂怪物的制作 在层级面板中,拖进一张蜜蜂怪物的图片,添加刚体组件,和合适的碰撞器,设置为触发。为这个对象设置两个动画,一个为停止动画,一个为飞行动画。摄像机还没有看到蜜蜂时,蜜蜂处于静止状态,当蜜蜂被摄像机照到时,蜜蜂处于飞行状态。蜜蜂飞到玩家的X位置时,投掷蜂蜜道具。附上蜜蜂的物理运动时脚本代码:
public void FixedUpdate()
{
if (isHit)
{
rig.velocity = new Vector2(masterFlyX, masterFlyY);
}
else if (isRest)
{
rig.velocity = Vector2.zero;
}
else if (!isStop && !isHit)
{
rig.velocity = new Vector2(flyspeedx, flyspeedy);
if (this.transform.position.x < PlayerController.instance.transform.position.x && honeyNum > 0)
{
Attack();
honeyNum--;
isRest = true;
StartCoroutine(ResetIsRest());
}
}
}
- 投叉小鼠的制作 与蜜蜂怪物类似,添加刚体组件和合适的碰撞器。为这个对象设置四个动画,停止动画,巡逻动画,攻击动画,跳跃动画。给这个对象加一个点进行射线是不是发现玩家,发现时,执行攻击逻辑播放攻击动画。这里附上部分代码:
public void Update()
{
if (state == State.Stop && this.transform.position.x < cam.position.x + xoffset)
{
//怪物行为被激活
state = State.Walk;
animator.SetTrigger("Walk");
}
else if (this.transform.position.x < cam.position.x - xoffset || this.transform.position.y > cam.position.y + yoffset)
{
Destroy(this.gameObject);
}
Walk();
Throw();
Escape();
}
Boss制作 在层级面包板中拖入Boss的图片,组件与合适的触发器,Boss主要两种状态,添加刚体,走动状态与停下攻击状态。这里附上Boss的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine; public class Boss : MonoBehaviour { public float range;
public float speed;
public float stopTime;
public float walkTime;
private float dir = 1;//方向正1表示坐标向右
private float leftrange;
private float rightrange;
private Rigidbody2D rig;
private Animator ani;
private SpriteRenderer spriterender;
private State state = State.Stop;
private float currentTime;
public GameObject puke;
public float pukeSpeed;
public int hp;
public int totalHp;
private bool isFlash = false;
public float flashTime;
public Color flashColor;
public Color normalColor;
public Color dangerColor;
public float destoryTime; public GameObject bosspuke;
public GameObject kill; public enum State
{
Stop,
Walk,
Attack,
Die
}
// Use this for initialization
void Start () {
rig = this.GetComponent<Rigidbody2D>();
ani = this.GetComponent<Animator>();
spriterender = this.GetComponent<SpriteRenderer>();
leftrange = this.transform.position.x - range;
rightrange = this.transform.position.x + range;
} void Update () { ChangeColor();
ani.SetInteger("state", (int)state);
if (state == State.Stop)
{
StartCoroutine(CancelStop());
}
else if (state == State.Walk)
{
currentTime += Time.deltaTime;
if (currentTime > walkTime)
{
currentTime = 0f;
state = State.Attack;
}
else
{
if (this.transform.position.x > rightrange)
{
transform.localScale = new Vector3(-1F, 1F, 1F);
dir = -1F;
}
if (this.transform.position.x < leftrange)
{
transform.localScale = new Vector3(1F, 1F, 1F);
dir = 1F;
}
}
}
else if (state == State.Die)
{
ani.enabled = false;
this.GetComponent<Collider2D>().enabled = false;
Destroy(this.gameObject, destoryTime);
}
} public void ChangeColor()
{
if (isFlash)
spriterender.color = flashColor;
else
spriterender.color = normalColor;
if (hp <= 1)
{
spriterender.color = dangerColor;
if (hp <= 0)
{
state = State.Die;
}
}
} public void FixedUpdate()
{
if (state == State.Stop || state == State.Attack)
{
rig.velocity = Vector2.zero;
}
else if (state == State.Walk)
{
rig.velocity = new Vector2(dir * speed, 0);
}
else if (state == State.Die)
{
rig.velocity = new Vector2(-3F,-1F);
}
} IEnumerator CancelStop()
{
yield return new WaitForSeconds(stopTime);
state = State.Walk;
} public void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "Player" &&
!PlayerController.instance.isFlash)
{
PlayerController.instance.Hurt();
PlayerInfo.instance.SubHp();
}
} public void ChangeWalkState()
{
state = State.Walk;
} public void Attack()
{
GameObject o = Instantiate(puke);
Vector2 start = this.transform.Find("firePos").position;
Vector2 end = GameObject.Find("player").transform.position;
o.transform.position = start;
Vector2 dir = end - start;
dir = dir.normalized;
o.GetComponent<Rigidbody2D>().velocity = dir*pukeSpeed;
Instantiate(bosspuke);
} public void Hit()
{
hp--;
isFlash = true;
StartCoroutine(ResetFlash());
if (hp<=0)
{
Instantiate(kill);
}
} IEnumerator ResetFlash()
{
yield return new WaitForSeconds(flashTime);
isFlash = false;
} }
游戏的大体思路就是这样,让我们看看成品的图片
FC经典游戏还原之:松鼠大作战2的更多相关文章
- C/C++编程笔记:C语言开发球球大作战(源码分享),你想试试吗?
游戏背景 <球球大作战>是Superpop一款自主研du发的免费手机网络游戏. 以玩家间的实时互动PK产生游戏乐趣为设计宗旨,通过简单的规则将玩家操作直接转化为游戏策略,体验智谋碰撞的战斗 ...
- 刺猬大作战(游戏引擎用Free Pascal写成,GUI用C++写成,使用SDL和Qt4)
游戏特性[编辑] 游戏引擎用Free Pascal写成,GUI用C++写成,使用SDL和Qt4[2]. 0.9.12开始支持实时动态缩放游戏画面. 个性化[编辑] 刺猬大作战有着高度定制性 游戏模式: ...
- cocos2d-x 3.2 它 三消游戏——万圣节大作战
***************************************转载请注明出处:http://blog.csdn.net/lttree************************** ...
- [知了堂学习笔记]_用JS制作《飞机大作战》游戏_第2讲(四大界面之间的跳转与玩家飞机的移动)
一.通过点击按钮事件,实现四大界面之间的跳转: (一)跳转的思路: 1.打开软件,只显示登录界面(隐藏游戏界面.暂停界面.玩家死亡界面) 2.点击微信登录(QQ登录)跳转到游戏界面,隐藏登录界面 3. ...
- Unity3D游戏贪吃蛇大作战源码休闲益智手机小游戏完整项目
<贪吃蛇大作战>一款休闲竞技游戏,不仅比拼手速,更考验玩家的策略. 视频演示: http://player.youku.com/player.php/sid/XMzc5ODA2Njg1Ng ...
- 用JS制作《飞机大作战》游戏_第2讲(四大界面之间的跳转与玩家飞机的移动)-陈远波
一.通过点击按钮事件,实现四大界面之间的跳转: (一)跳转的思路: 1.打开软件,只显示登录界面(隐藏游戏界面.暂停界面.玩家死亡界面) 2.点击微信登录(QQ登录)跳转到游戏界面,隐藏登录界面 3. ...
- java实现简单窗体小游戏----球球大作战
java实现简单窗体小游戏----球球大作战需求分析1.分析小球的属性: 坐标.大小.颜色.方向.速度 2.抽象类:Ball 设计类:BallMain—创建窗体 BallJPanel—画小 ...
- Android破解学习之路(十四)——【Unity3D】王牌大作战破解
一.前言 今天带来的是王牌大作战的破解教程,游戏下载的话,我是直接去TapTap官网下载的 支付宝内购破解用老套了,今天学点破解的新花样吧!! 二.支付宝内购破解 支付宝的内购破解已经很熟悉了, 直接 ...
- Expo大作战(十六)--expo结合firebase 一个nosql数据库(本章令我惊讶但又失望!)
简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...
随机推荐
- 【机器学习学习】SKlearn + XGBoost 预测 Titanic 乘客幸存
Titanic 数据集是从 kaggle下载的,下载地址:https://www.kaggle.com/c/titanic/data 数据一共又3个文件,分别是:train.csv,test.csv, ...
- Java与算法之(8) - 堆排序
堆是一种特殊的完全二叉树,其特点是所有父节点都比子节点要小,或者所有父节点都比字节点要大.前一种称为最小堆,后一种称为最大堆. 比如下面这两个: 那么这个特性有什么作用?既然题目是堆排序,那么肯定能用 ...
- Uva 10339 - Watching Watches【数论,暴力】
题目链接:10339 - Watching Watches 题意:两个时钟,一个每天慢a秒,一个每天慢b秒,问两钟重新相遇的时刻 1圈有12 * 60 * 60秒,然后1圈 / abs(a - b), ...
- c语言基础学习07_关于指针的复习
============================================================================= 指针变量之间赋值是需要兼容的. 例如:int ...
- c++(非递归排序)
在上面一篇博客当中,我们发现普通查找和排序查找的性能差别很大.作为一个100万的数据,如果使用普通的查找方法,那么每一个数据查找平均下来就要几十万次,那么二分法的查找呢,20多次就可以搞定.这中间的差 ...
- 五 : springMVC拦截器
springMVC拦截器的实现一般有两种方式 第一种方式是要定义的Interceptor类要实现了Spring的HandlerInterceptor 接口 第二种方式是继承实现了HandlerInte ...
- 修改nopCommerce中的实体
对已有实体增加一个属性(对Category增加一个SomeNewProperty) 最近在研究nopcommerce,这里是对官网上文档的学习 ...
- Bad Request (Invalid Hostname)什么意思? 200
一般来说与你电脑无关 错误请求(无效主机名) 域名已绑定主机 但主机未绑定域名就会出现这种情况! 总结页面出现Bad Request (Invalid Hostname)的原因: 1.如果确定域名已经 ...
- 织梦dedeCMS留言薄
dedeCMS留言薄模塊名爲guestbook, 留言薄模板:/templets/plus/guestbook.htm; 留言回覆模板: 管理員回覆調用/templets/plus/guestbook ...
- 用PHPMailer在本地win环境,可以接收到邮件和附件,但在linux环境只能接收邮件信息接不到附件,是我的路
解决了,Linux区分大小写问题