版权声明:

  • 本文原创发布于博客园"优梦创客"的博客空间(网址:http://www.cnblogs.com/raymondking123/)以及微信公众号“优梦创客”(微信号:unitymaker)
  • 您可以自由转载,但必须加入完整的版权声明!

松鼠大作战游戏制作

  1. 游戏介绍 1990年,经迪士尼授权,由日本卡普空(Capcom)电视游戏公司制作的基于任天堂FC主机的电视游戏《松鼠大作战》出版发行。游戏延用迪士尼动画片《松鼠大作战》里的两只可爱花栗鼠Chip and Dale(奇奇和蒂蒂),从寻找小猫咪的委托任务开始,到摆脱肥猫陷阱与其一决高下为故事内容。可单人玩又可双人对战,可互助又可互攻,流畅度,创造力与可玩性均为同类游戏中的领军者。

    1993年,卡普空制作发行了《松鼠大作战2》再一次将经典搬到屏幕。除了制作更加精良外,故事也更加惊险刺激。

  2. 场景搭建 将场景中的图片首尾拼接,在合适的位置设置碰撞器与平台控制器

  3. 主角Chip登场 将主角图片拖入层级面板上,命名为“player”,添加刚体2D组件和合适的碰撞器组件。为player设置子节点hand,添加碰撞器组件设置为触发,用于触碰箱子的判断;设置子节点foot,调整恰当的位置,用于跳跃,以及主角跳跃动画的条件判定;再设置两个节点用于主角投掷物品发射的位置。

  4. 主角Chip动画制作 打开动画控制器,创建主角的Idel动画,在合适的时间轴拖上相应的主角图片,重复操作将主角的动画设置好。

  5. 主角Chip基本动作实现 为player添加脚本组件,命名为PlayerController,主角的移动需要通过输入设备为其提供指令,并进行相应的操作,Unity中Input可以获得这些操作。horizontal与vertical均是浮点数范围为-1至1,方向与卡迪尔坐标相同,isPressjump与isPressFire均为Bool类型。通过射线的方式我们可以判断处人物是否在跳跃状态下。Chip的移动与跳跃我们通过物理运动进行操作,在FixedUpdate()下进行代码操作;

    1.  
  1. isJump = !(Physics2D.Linecast(transform.position, foot.position, 1 << LayerMask.NameToLayer("Map")) || Physics2D.Linecast(transform.position, foot.position, 1 << LayerMask.NameToLayer("Box")));
  2. horizontal = Input.GetAxis("Horizontal");//上下
  3. vertical = Input.GetAxis("Vertical");//左右
  4. isPressjump = Input.GetButtonDown("Jump");//跳跃
  5. isPressFire = Input.GetButtonDown("Fire1");//攻击
  6. void FixedUpdate()
  7. {
  8. if (isControl)
  9. {
  10. if (!isDown && isPressjump && !isJump)
  11. {
  12. if (rig.velocity.y < 1F)
  13. {
  14. rig.AddForce(Vector3.up * force);
  15. }
  16. isJump = true;
  17. }
  18. if (isDown)
  19. {
  20. rig.velocity = new Vector3(0, rig.velocity.y);
  21. }
  22. else
  23. {
  24. rig.velocity = new Vector3(speed * horizontal, rig.velocity.y);
  25. }
  26. }
  27. }
  1. 主角Chip攻击实现 与射击游戏的区别,在本作中,CHip是通过搬物品再投掷出去进行攻击的。这里的设想是将场景的箱子与投掷的箱子区别开来,方便判断。player下的节点hand添加脚本,在OnTriggerStay2D下调用MoveBox方法,isHandBox判断手中是否有箱子,isThrow则判断有没有进行投掷操作。这里贴出主要代码:
  1. public void MoveBox(Collider2D collision)
  2. {
  3. //搬箱子
  4. if (!isDown && collision.tag.StartsWith("Box") && isPressFire && !isHandBox && !isThrow)
  5. {
  6. Destroy(collision.gameObject);//场景中的箱子被销毁了,游戏中松鼠头顶上的箱子仅仅是外观的区别
  7. isHandBox = true;
  8. animator.SetTrigger("moveBox");
  9. Instantiate(movebox);
  10. }
  11. }
  12. private void ThrowBox()
  13. {
  14. if (isHandBox && isPressFire)
  15. {
  16. isHandBox = false;
  17. isThrow = true;
  18. GameObject o = Instantiate(throwingbox);//投掷箱子时实例化一个投掷的箱子(与场景的箱子功能不同)
  19. if (!isDown)
  20. {
  21. o.transform.position = transform.Find("fireposition").position;
  22. }
  23. else
  24. {
  25. o.transform.position = transform.Find("downfireposition").position;
  26. }
  27. o.GetComponent<ThrowingBox>().Throw(transform.localScale.x, vertical);
  28. StartCoroutine(ResetIsThrow());
  29. animator.SetTrigger("throwbox");
  30. Instantiate(throwbox);
  31. }
  32. }
  1. 主角Chip动画控制器动画控制 给出合适的条件进行动画转换,本作动作丰富,通过多次尝试,本作动作才比较连贯。

  2. 场景箱子的制作 将箱子图片拖进层级面板中,为它设置合适的碰撞器,做成预制体备用。

  3. 投掷箱子的制作 与场景箱子类似,设置触发器,另外需要增加刚体2D组件与脚本,脚本主要用于处理与怪物接触的交互,同样也制成预制体。

  4. 其他道具的制作 其他道具有花(吃了一定的数量可以奖励生命);蜂蜜(蜜蜂怪物攻击的道具,玩家触碰会造成伤害);坚果(若主角受伤,吃到该道具会增加1点Hp值);叉子(投掷小鼠投掷的道具,玩家触碰会造成1点Hp伤害)。

  5. 一些其他的制定 在本场景的末尾添加添加合适的触发器,绑上脚本可以通过下一个场景;在悬崖处也添加一个长条的触发器,用于触发主角死亡判定并重新加载本场景。

  6. 蜜蜂怪物的制作 在层级面板中,拖进一张蜜蜂怪物的图片,添加刚体组件,和合适的碰撞器,设置为触发。为这个对象设置两个动画,一个为停止动画,一个为飞行动画。摄像机还没有看到蜜蜂时,蜜蜂处于静止状态,当蜜蜂被摄像机照到时,蜜蜂处于飞行状态。蜜蜂飞到玩家的X位置时,投掷蜂蜜道具。附上蜜蜂的物理运动时脚本代码:

  1. public void FixedUpdate()
  2. {
  3. if (isHit)
  4. {
  5. rig.velocity = new Vector2(masterFlyX, masterFlyY);
  6. }
  7. else if (isRest)
  8. {
  9. rig.velocity = Vector2.zero;
  10. }
  11. else if (!isStop && !isHit)
  12. {
  13. rig.velocity = new Vector2(flyspeedx, flyspeedy);
  14. if (this.transform.position.x < PlayerController.instance.transform.position.x && honeyNum > 0)
  15. {
  16. Attack();
  17. honeyNum--;
  18. isRest = true;
  19. StartCoroutine(ResetIsRest());
  20. }
  21. }
  22. }
  1. 投叉小鼠的制作 与蜜蜂怪物类似,添加刚体组件和合适的碰撞器。为这个对象设置四个动画,停止动画,巡逻动画,攻击动画,跳跃动画。给这个对象加一个点进行射线是不是发现玩家,发现时,执行攻击逻辑播放攻击动画。这里附上部分代码:
  1. public void Update()
  2. {
  3. if (state == State.Stop && this.transform.position.x < cam.position.x + xoffset)
  4. {
  5. //怪物行为被激活
  6. state = State.Walk;
  7. animator.SetTrigger("Walk");
  8. }
  9. else if (this.transform.position.x < cam.position.x - xoffset || this.transform.position.y > cam.position.y + yoffset)
  10. {
  11. Destroy(this.gameObject);
  12. }
  13. Walk();
  14. Throw();
  15. Escape();
  16. }
  1. Boss制作 在层级面包板中拖入Boss的图片,组件与合适的触发器,Boss主要两种状态,添加刚体,走动状态与停下攻击状态。这里附上Boss的代码:

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class Boss : MonoBehaviour {
    5. public float range;
    6. public float speed;
    7. public float stopTime;
    8. public float walkTime;
    9. private float dir = 1;//方向正1表示坐标向右
    10. private float leftrange;
    11. private float rightrange;
    12. private Rigidbody2D rig;
    13. private Animator ani;
    14. private SpriteRenderer spriterender;
    15. private State state = State.Stop;
    16. private float currentTime;
    17. public GameObject puke;
    18. public float pukeSpeed;
    19. public int hp;
    20. public int totalHp;
    21. private bool isFlash = false;
    22. public float flashTime;
    23. public Color flashColor;
    24. public Color normalColor;
    25. public Color dangerColor;
    26. public float destoryTime;
    27. public GameObject bosspuke;
    28. public GameObject kill;
    29. public enum State
    30. {
    31. Stop,
    32. Walk,
    33. Attack,
    34. Die
    35. }
    36. // Use this for initialization
    37. void Start () {
    38. rig = this.GetComponent<Rigidbody2D>();
    39. ani = this.GetComponent<Animator>();
    40. spriterender = this.GetComponent<SpriteRenderer>();
    41. leftrange = this.transform.position.x - range;
    42. rightrange = this.transform.position.x + range;
    43. }
    44. void Update () {
    45. ChangeColor();
    46. ani.SetInteger("state", (int)state);
    47. if (state == State.Stop)
    48. {
    49. StartCoroutine(CancelStop());
    50. }
    51. else if (state == State.Walk)
    52. {
    53. currentTime += Time.deltaTime;
    54. if (currentTime > walkTime)
    55. {
    56. currentTime = 0f;
    57. state = State.Attack;
    58. }
    59. else
    60. {
    61. if (this.transform.position.x > rightrange)
    62. {
    63. transform.localScale = new Vector3(-1F, 1F, 1F);
    64. dir = -1F;
    65. }
    66. if (this.transform.position.x < leftrange)
    67. {
    68. transform.localScale = new Vector3(1F, 1F, 1F);
    69. dir = 1F;
    70. }
    71. }
    72. }
    73. else if (state == State.Die)
    74. {
    75. ani.enabled = false;
    76. this.GetComponent<Collider2D>().enabled = false;
    77. Destroy(this.gameObject, destoryTime);
    78. }
    79. }
    80. public void ChangeColor()
    81. {
    82. if (isFlash)
    83. spriterender.color = flashColor;
    84. else
    85. spriterender.color = normalColor;
    86. if (hp <= 1)
    87. {
    88. spriterender.color = dangerColor;
    89. if (hp <= 0)
    90. {
    91. state = State.Die;
    92. }
    93. }
    94. }
    95. public void FixedUpdate()
    96. {
    97. if (state == State.Stop || state == State.Attack)
    98. {
    99. rig.velocity = Vector2.zero;
    100. }
    101. else if (state == State.Walk)
    102. {
    103. rig.velocity = new Vector2(dir * speed, 0);
    104. }
    105. else if (state == State.Die)
    106. {
    107. rig.velocity = new Vector2(-3F,-1F);
    108. }
    109. }
    110. IEnumerator CancelStop()
    111. {
    112. yield return new WaitForSeconds(stopTime);
    113. state = State.Walk;
    114. }
    115. public void OnTriggerEnter2D(Collider2D collision)
    116. {
    117. if (collision.gameObject.tag == "Player" &&
    118. !PlayerController.instance.isFlash)
    119. {
    120. PlayerController.instance.Hurt();
    121. PlayerInfo.instance.SubHp();
    122. }
    123. }
    124. public void ChangeWalkState()
    125. {
    126. state = State.Walk;
    127. }
    128. public void Attack()
    129. {
    130. GameObject o = Instantiate(puke);
    131. Vector2 start = this.transform.Find("firePos").position;
    132. Vector2 end = GameObject.Find("player").transform.position;
    133. o.transform.position = start;
    134. Vector2 dir = end - start;
    135. dir = dir.normalized;
    136. o.GetComponent<Rigidbody2D>().velocity = dir*pukeSpeed;
    137. Instantiate(bosspuke);
    138. }
    139. public void Hit()
    140. {
    141. hp--;
    142. isFlash = true;
    143. StartCoroutine(ResetFlash());
    144. if (hp<=0)
    145. {
    146. Instantiate(kill);
    147. }
    148. }
    149. IEnumerator ResetFlash()
    150. {
    151. yield return new WaitForSeconds(flashTime);
    152. isFlash = false;
    153. }
    154. }
  2. 游戏的大体思路就是这样,让我们看看成品的图片











FC经典游戏还原之:松鼠大作战2的更多相关文章

  1. C/C++编程笔记:C语言开发球球大作战(源码分享),你想试试吗?

    游戏背景 <球球大作战>是Superpop一款自主研du发的免费手机网络游戏. 以玩家间的实时互动PK产生游戏乐趣为设计宗旨,通过简单的规则将玩家操作直接转化为游戏策略,体验智谋碰撞的战斗 ...

  2. 刺猬大作战(游戏引擎用Free Pascal写成,GUI用C++写成,使用SDL和Qt4)

    游戏特性[编辑] 游戏引擎用Free Pascal写成,GUI用C++写成,使用SDL和Qt4[2]. 0.9.12开始支持实时动态缩放游戏画面. 个性化[编辑] 刺猬大作战有着高度定制性 游戏模式: ...

  3. cocos2d-x 3.2 它 三消游戏——万圣节大作战

    ***************************************转载请注明出处:http://blog.csdn.net/lttree************************** ...

  4. [知了堂学习笔记]_用JS制作《飞机大作战》游戏_第2讲(四大界面之间的跳转与玩家飞机的移动)

    一.通过点击按钮事件,实现四大界面之间的跳转: (一)跳转的思路: 1.打开软件,只显示登录界面(隐藏游戏界面.暂停界面.玩家死亡界面) 2.点击微信登录(QQ登录)跳转到游戏界面,隐藏登录界面 3. ...

  5. Unity3D游戏贪吃蛇大作战源码休闲益智手机小游戏完整项目

    <贪吃蛇大作战>一款休闲竞技游戏,不仅比拼手速,更考验玩家的策略. 视频演示: http://player.youku.com/player.php/sid/XMzc5ODA2Njg1Ng ...

  6. 用JS制作《飞机大作战》游戏_第2讲(四大界面之间的跳转与玩家飞机的移动)-陈远波

    一.通过点击按钮事件,实现四大界面之间的跳转: (一)跳转的思路: 1.打开软件,只显示登录界面(隐藏游戏界面.暂停界面.玩家死亡界面) 2.点击微信登录(QQ登录)跳转到游戏界面,隐藏登录界面 3. ...

  7. java实现简单窗体小游戏----球球大作战

    java实现简单窗体小游戏----球球大作战需求分析1.分析小球的属性: ​ 坐标.大小.颜色.方向.速度 2.抽象类:Ball ​ 设计类:BallMain—创建窗体 ​ BallJPanel—画小 ...

  8. Android破解学习之路(十四)——【Unity3D】王牌大作战破解

    一.前言 今天带来的是王牌大作战的破解教程,游戏下载的话,我是直接去TapTap官网下载的 支付宝内购破解用老套了,今天学点破解的新花样吧!! 二.支付宝内购破解 支付宝的内购破解已经很熟悉了, 直接 ...

  9. Expo大作战(十六)--expo结合firebase 一个nosql数据库(本章令我惊讶但又失望!)

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...

随机推荐

  1. kafka Topic 与 Partition

    Topic在逻辑上可以被认为是一个queue队列,每条消息都必须指定它的topic,可以简单理解为必须指明把这条消息放进哪个queue里.为 了使得Kafka的吞吐率可以水平扩展,物理上把topic分 ...

  2. Asp.net Mvc 与 Web Api生命周期对比

    完整的生命周期比较复杂,对细节感兴趣的同学可购买老A的图书学习:传送门 本文只简单讲述路由注册.controller创建.action选择的3个主逻辑线,其他的内容大家可自己阅读相应的代码 先上二者单 ...

  3. cs231n spring 2017 lecture3 Loss Functions and Optimization 听课笔记

    1. Loss function是用来量化评估当前预测的好坏,loss function越小表明预测越好. 几种典型的loss function: 1)Multiclass SVM loss:一般的S ...

  4. 【Java学习笔记之五】java数组详解

    数组 概念 同一种类型数据的集合.其实数组就是一个容器. 数组的好处 可以自动给数组中的元素从0开始编号,方便操作这些元素. 格式1: 元素类型[] 数组名 = new 元素类型[元素个数或数组长度] ...

  5. [国嵌笔记][023][ARM寻址方式]

    寻找方式 1.处理器根据指令中给出的信息来找到指令所需操作数的方式 2.立即数寻址 操作数本身在指令中给出,立即数前加”#”表示立即数寻址,操作数在指令中 3.寄存器寻址 利用寄存器中的数值作为操作数 ...

  6. thinkphp无法加载控制器:Admin

    在使用thinkphp时,通过某入口文件访问其他非默认的模块(比如Admin模块),出现报错: 无法加载控制器:Admin 原因:入口文件(比如index.php)中定义了绑定某个具体的模块 如:de ...

  7. OKMX6Q libx264交叉编译

    最开始使用的是最新版x264-snapshot-20171119-2245 配置使用: ./configure --host=arm-linux --cross-prefix=arm-linux- - ...

  8. asp.net -mvc框架复习(7)-基于MVC搭建用户登录项目框架

    整体框架: 一.搭建Model层 1.添加通用数据访问类 2.添加实体类(封装和传递数据,和数据库中数据表对应) 3.添加数据访问类(通常和实体类同名,但是后缀名发生改变) 二.搭建控制器层Contr ...

  9. Android带有粘性头部的ScrollView

    前言,一天在点外卖的时候,注意到饿了么列表页的滑动效果不错,但是觉得其中的手势滑动还是挺复杂的,正好又碰到了在熟悉Touch事件的理解当中,所以就抽空对着饿了么的列表页面尝试写写这个效果 1.先贴一个 ...

  10. Docker问题: Layer already being pulled by another client. Waiting.什么原因

    问题描述:Layer already being pulled by another client. Waiting. 问题分析:这是 1.8版本的一个bug,会在1.9版本中修复.http://st ...