自从上次跟着敲了官方示例拾荒者之后,开始对Unity制作2D游戏感兴趣了起来,虽然本文标题叫做Unity3D学习日记。但是Unity其实本来名字里是没有3D这俩字的……很有名的雨血前传 蜃楼就是一个使用了Unity来开发的2D游戏。
用Unity做2D游戏有几个优势(对我来说):1.可以继续使用我感兴趣的Unity引擎,不用转回局限性较大的cocos2d,当然,这点是废话。2.Unity的跨平台机能依然是去选择它的很重要的一个因素。3.还没想好。
前几天本来想再琢磨一个官方的2D游戏样例,就是一个土豆人扛着火箭筒杀外星人的,很多视频教程都喜欢用它来做实例,而我觉得国内的讲师节奏太慢,选择了观看官方原版视频,果不其然,听力受限。故找到了一位听译的视频作者制作的视频观看,再次非常感谢,来自B站的风农同学。然后偶然间看到了另一个人发的自制视频,叫做教你用Unity 20分钟做出FlappyBird,我点进去刚看了2分钟立马关了视频,不是因为有急事,而是我觉得如果我独立思考着制作也许会更有意思一些。这个想法也来源于一直是在看视频学习,而没有亲自上手制作过一款完整的游戏。当时已是深夜,于是决定第二天在一天之内自己独立制作一个仿照FlappyBird的小游戏。
废话说了那么多,下面开始简单展示一下制作的流程。
第一步,绘制可能会用到的Sprite。
既然是自制的小游戏,自然不想使用网上的素材,于是开始直接用电脑绘制一些简单的几何图形组成的Sprite。
↑首先是鸟的Sprite,想要未来添加一个简单的动画,所以眼球鸟嘴分开做了其实就是几个几何图形
↑ 在FlappyBird中作为障碍物的迷之管道。
↑ MyFlappyBird标题。。弄这么霸气是个人的恶趣味
↑ 最后是来自我的签名Pb。。(CSDN的这个编辑器使用直接上传的图片好像并不能改大小,每张上传上来都奇大无比,下次试试Markdown编辑器)
↑ 最后制作的作为背景云朵的Sprite(由于本来Png背景是透明的,云是白色的,上传之后背景自动变为白色,所以现在是一个只能看见灰色圆球的情况。。)
第二步,在Unity里制作可能要使用到的Prefab
由于是整个游戏做完之后才截的图,所以上面挂了很多组件,其实都是后来陆续一点点添的,最开始就只有Rigibody2d和一个圆形Collider2d,
为什么上面没有SpriteRenderer呢?
嗯。是因为Bird由切割过的Sprite组合而成,每个子组件分别使用SpriteRenderer渲染。
接下来是用于阻挡小鸟用的管道的Prefab:
没什么特别的。3个碰撞体,上下管子各一个,中间空气一个用于加分。
第三步,写脚本。
大概构思使用3个脚本,Bird身上一个用于每次点击时给鸟一个向上的力,以及检测是否碰撞到了管道,来判断游戏是否结束,还有检测是否通过了管道中间的部分来加分,以及最后添加上的一部分音效,代码如下:
using UnityEngine;
using System.Collections;
public class Bird : MonoBehaviour {
public float flyforce = 500f;
private Rigidbody2D rbody2d;
public bool isGameOver = false;
public int score;
public AudioSource addScore;
public AudioSource die;
CircleCollider2D circleCollider2d;
void Start ()
{
rbody2d = GetComponent();
//addScore = GetComponent();
circleCollider2d=GetComponent();
}
void Update () {
if (Input.GetMouseButtonDown(0))
{
fly();
}
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag=="Wall")
{
gameover();
}
}
public void OnTriggerExit2D(Collider2D collision)
{
if (collision.gameObject.tag == "MainCamera")
{
gameover();
}
if (collision.gameObject.name=="wall_scoreget")
{
score++;
addScore.Play();
}
}
void gameover()
{
//print("Game Over");
isGameOver = true;
circleCollider2d.enabled = false;
rbody2d.AddForce(new Vector2(0, flyforce*2)); //在死亡时会关闭碰撞,并使鸟向上跳一下,是模仿马里奥的死亡模式
die.Play();
}
void fly()
{
if(!isGameOver) //没死才能调用fly使鸟向上飞
{
rbody2d.AddForce(new Vector2(0, flyforce));
//print("Called a fly function");
}
}
}
using UnityEngine;
using System.Collections;
public class Wall : MonoBehaviour {
public float movespeed = 5f;
void Start () {
}
public void OnTriggerExit2D(Collider2D collision)
{
if (collision.gameObject.name == "Out")
{
print("des");
}
}
void Update () {
gameObject.transform.Translate(-movespeed * Time.deltaTime, 0, 0);
if (gameObject.transform.position.x<-13f)
{
Destroy(gameObject);
}
}
}
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class Main : MonoBehaviour {
public Bird birdscript;
public GameObject wall;
private GameObject bird;
private Rigidbody2D birdrbody2d;
bool gamestart = false;
bool isGameOver = false;
bool readyForRestart = false;
int showscore;
public Text score;
public GameObject gameovertext;
public Text startTip;
public GameObject restartTip;
public GameObject title;
void Start () {
bird=GameObject.Find("Bird");
birdrbody2d = bird.GetComponent();
birdrbody2d.gravityScale = 0; //在游戏刚开始时,使鸟的重力缩放为0,这样在第一次单击之前鸟会保持一个漂浮的状态
InvokeRepeating("creatwall",0f,1f);
}
// Update is called once per frame
void Update () {
if (gamestart==false&&Input.GetMouseButtonDown(0))
gamestart = true;
if (gamestart)
{
birdrbody2d.gravityScale = 1; //第一次单击游戏开始,鸟开始拥有1倍重力
startTip.text = "";
title.SetActive(false);
}
isGameOver=birdscript.isGameOver;
showscore = birdscript.score;
score.text = "分数:" + showscore; //刷新得分GUI
if (isGameOver)
{
//print("game over in main");
gameover();
}
if (readyForRestart&&Input.GetMouseButtonDown(0))
{
SceneManager.LoadScene(0); //当准备好重开之后单击才会重新加载场景
}
}
void creatwall()
{
if (gamestart&&!isGameOver)
{
Instantiate(wall, new Vector3(6.2f + Random.Range(-1f, 1f), Random.Range(-1.82f, 1.82f), 0), Quaternion.identity); //在屏幕右边实例化管道
//位置具有一定随机性
}
}
void gameover()
{
gameovertext.SetActive(true);
birdrbody2d.gravityScale = 7; //在死亡时更改鸟的重力,使其快速下落
Invoke("prepareForRestart", 2); //在玩家死亡之后2秒才可以重开游戏
}
void prepareForRestart()
{
restartTip.SetActive(true); //显示重开游戏提示GUI
readyForRestart = true;
}
}
这里就不详细讲了,背景就是之前画的云朵,也给云朵添加了一个会缓慢上下运动的动画,然后给鸟添加了一个眼球左右动的动画,鸟嘴上下微动的动画,都是作为默认的状态,为了不让鸟显得太过于死板。
音效可能是唯一非原创的部分,因为确实不会制作音效。。于是从网上下载了FALCOM双星物语里经典的村庄BGM作为BGM,马里奥中起跳的音效作为加分的音效,以及一个音量递减的低音作为死亡的音效。具体如何添加可以在代码中看出来,并没有像一般的做法专门添加一个音频管理器,因为音效较少,可以选择直接挂在在对象身上。
第五步,测试。效果可以见下GIF:
(由于GIF录制的原因,实际游戏运行速度比GIF中展示的要快,而且录制的过程中有点掉帧)
第六步,移动端适配调整,这一步暂且还没有做好,因为新换了Unity 5.X的版本,对安卓SDK要求23以上,之前用的22的不行了,新的SDK还没下好,大概导出到安卓平台的话Input.GetMouseDown()用不了吧,当然也还没测试,如果不行可以换成Input.GetTouch(0).Phase==TouchPhase.Down来试试。
这里适配或许会更新,或许会在下一篇里写到吧。
大概整个MyFlappyBird就是这样做出来的,大概前天用了4个小时吧,这篇总结是前天晚上开始写,然后昨天写了一天论文,今天中午继续写完了这篇学习日记。