其他分享
首页 > 其他分享> > 用unity做一个简易的flappy bird

用unity做一个简易的flappy bird

作者:互联网

最近正学习unity,想起很久没写博客了,打算试着写篇用unity制作一个简易的flappy bird。

 

0.最初的准备工作

首先先弄两个足够长的cube充当上下边界,将它们分别放在摄像机看到的视角里的上面和下面。注意除摄像机外,其他的游戏物体z轴上的坐标要保持一致,因为我们要将其放在同一平面上。

再创建一个球和方块,之后就由它们来充当我们的小鸟和管道了(毕竟只是个简易的小游戏)。

 

1.玩家的移动

在我们的游戏中,实际上我们只要控制小鸟的上下移动即可,左右的移动将由地图的移动来实现。

首先,我们需要给小鸟添加刚体,让其可以随重力的影响而下落。

之后我们创建一个脚本Bird,用来控制小鸟的飞行。

我们需要先获取脚本的刚体。

Rigidbody rigidbody;
void Awake()
{
    rigidbody = GetComponent<Rigidbody>();
}

当每次按下空格时,我们需要小鸟有个向上飞行的效果。我们可以把刚体的速度改成一个向上的向量。我们用GetKeyDown来检测按键输入,并放到update里更新bird。

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space)) {
        rigidbody.velocity = Vector3.up;
    }
}

之后我们可以将脚本拖动到小球上。可以看到小球虽然有了反应,但并不明显,更像是在空中停顿了一下。

我们可以给小球增加一个速度,来改变向上速度的大小。

public float speed = 10f;

之后将向上的向量乘上这个速度。

rigidbody.velocity = Vector3.up * speed;

这时小球的反应比之前强烈了很多,但是又跳的太高了。我们可以直接在Inspector上挂着的脚本里修改这个速度,选择一个自己觉得合适的大小即可。

小球的工作差不多到这里就结束了。

 

2.管道的移动

接着我们要让小球碰到边界和管道即死。我们可以写一个脚本,挂在边界和管道上。当检测到小球碰撞时删掉小球。

但首先,让我们来将小球的Tag改为Player。

创建脚本DeadBox,用OnCollisionEnter来检测小球的碰撞,当碰撞对象Tag为Player时,将对象删除。

public class DeadBox : MonoBehaviour
{
    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.tag == "Player") {
            Destroy(collision.gameObject);
        }
    }
}

给我们的边界和管道挂上这个脚本,我们就可以看到效果了。

接下来我们需要完成管道的移动,创建新脚本Move。有了刚刚的经验,同样的,我们先设一个speed控制速度。

public class Move : MonoBehaviour
{
    public float speed = 10f;
}

 之后为了让移动更稳定,减少帧率的影响,我们除了speed外,还需要再乘一个Time.deltaTime。

void Update()
{
    transform.localPosition += -Vector3.right * speed * Time.deltaTime;
}

将它挂到管道上,我们就可以看到管道向左移动了。

 

3.地图的生成

接下来我们需要能够随机的生成地图,好让地图上不断产生随机长度的管道,可以让游戏一直持续下去直到玩家死亡。

首先先将管道拖到下方的文件夹中,成为一个预制体。再将原先的管道删除掉。

之后我们创建一个新的脚本,叫Control,用来控制游戏流程。

首先需要一个GameObject属性,来选择我们的预制体。还有两个Vector3,来存放管道的上下生成点。

public class Control : MonoBehaviour
{
    public GameObject prefab;
    public Vector3 upper, lower;
}

我们在游戏界面创造一个空物体GameControl来管理我们的游戏。将Control脚本挂在上面。

将预制体挂在脚本上。根据上下边界的位置,选择合适的生成点填在upper, lower中,来生成我们的管道。

我们可以先在Awake里测试一下生成的位置,使用Instantiate函数可以实例预制体。

private void Awake()
{
    GameObject p;
    p = Instantiate(prefab, upper, Quaternion.identity);
    p = Instantiate(prefab, lower, Quaternion.identity);
}

选择好合适的生成位置后,我们将原先Awake里的测试代码删掉。接下来我们需要让游戏每隔一段时间生成一条管道。 我们需要新的属性delayTime和lastTime,分别代表我们每隔多长时间生成新的管道和距离上次生成管道间隔的时间。

public float delayTime = 2f;
float lastTime;

private void Awake()
{
    lastTime = 0f;
}

 每次update,我们给lastTime加上间隔的时间。当lastTime大于delayTime,即代表我们需要生成新的管道了。

private void Update()
{
    lastTime += Time.deltaTime;
    if (lastTime >= delayTime) {
        lastTime = 0f;
        Instantiate(prefab, lower, Quaternion.identity);
    }
}

此时我们已经可以固定时间生成管道了,接下来我们需要添加一些随机元素。首先,让我们随机选择管道是在上面还是下面生成。

Vector3 point = (Random.Range(0, 2) == 0) ? lower : upper;
Instantiate(prefab, point, Quaternion.identity);

接着我们要改变管道的大小,首先设一个最大高度。

public float maxHeight = 10f;

对管道生成的部分进行修改

Vector3 point = (Random.Range(0, 2) == 0) ? lower : upper;
GameObject pipe = Instantiate(prefab, point, Quaternion.identity);
pipe.transform.localScale = new Vector3(1f, Random.Range(1f, maxHeight), 1f);

现在我们就完成了一个可以随机生成管道的地图了。 

 

4.游戏得分

为了能够计算得分,现在我们需要对管道进行一些小修改。

首先,先创建一个新的空物体,并将原本的管道预制体挂到上面。注意挂上预制体后将其poistion改为(0,0,0)。(图中我将挂上的预制体改名为了Body)

复制这个Body,命名为Trigger,同样挂在新的空物体上。删除它除了碰撞器外的全部组件,勾选上碰撞器的Is Trigger,并将size的y值调大,让其上下任意一边的高度可以大于等于游戏界面的高度。我们可以删去原来的管道预制体了(注意预制体删除后,要将原来的子物体unpake,对着子物体->Prefab->Unpake即可)。

我们将新的管道保存为预制体,并重新在GameControl的脚本中挂上。将游戏界面的管道删除。

现在,我们需要写一个新的脚本Trigger。我们将把它挂在Trigger上,并在Player离开他的碰撞体时增加游戏得分。

类似与之前的DeadBox,但我们这次使用的是OnTriggerExit函数。

private void OnTriggerExit(Collider other)
{
    if (other.gameObject.tag == "Player") {
        
    }
}

在增加游戏得分前,我们先要在一个地方记录游戏得分。修改Control脚本。

static int score;
private void Awake()
{
    lastTime = 0f;
    score = 0;
}
public static int GetScore() { return score; }
public static void SetScore() { score++; }

再回来修改Trigger的OnTriggerExit

private void OnTriggerExit(Collider other) {
    if (other.gameObject.tag == "Player") {
        Control.SetScore();
    }
}

将Trigger挂在预制体的Trigger上,并给预制体挂上之前写的Move,把预制体的Body上的Move删掉。

现在,我们已经有了得分。但我们还需要将得分直观的显示给玩家。

右键UI->TEXT,命名为Score Text。将其拜访到合适的位置,并在右边修改Font Size,Alignment等,选择合适的字体大小,居中方式等。

在Control脚本头部添加using UnityEngine.UI;,添加public Text scoreText;,并在游戏界面将刚刚新建的文本框拖到脚本上。

在Control的update函数中添加scoreText.text = "Score:" + score.ToString();

现在,我们可以在游戏中看到分数了。

 

5.一些小细节

现在这个简陋的小游戏已经基本完成,但还要再修改些小细节。

首先,我们要修改Move脚本,让生成的管道可以定时消失,防止随游戏进行生成的管道过多。

public class Move : MonoBehaviour
{
    public float speed = 10f;
    public float delayTime = 5f;

    float lastTime;

    void Update()
    {
        lastTime += Time.deltaTime;
        transform.localPosition += -Vector3.right * speed * Time.deltaTime;
        if (lastTime >= delayTime) {
            Destroy(this.gameObject);
        }
    }
}

我们可以再给小球涂点简单的颜色,让游戏看着更顺眼一些。

右键->create->Metarial,创建一个新的材质,选择一个颜色,再将它挂到小球上。

我们也可以用同样的方法来给边界和管道加点颜色。

PS:在给管道涂色后,会看到边界和管道的颜色相互交错。我的方法是将边界Poistion的z值-0.01,让其离摄像机更近一点。

最后,由于我们用的是3d画面,其视角可能影响我们对碰撞的判断。我们也可以将摄像机的Projection改为Orthographic呈现更好的2d效果。

这篇博客就到这里。因为我也是初学者,游戏的实现方式可能不怎么好,只是记录一下自己的实现方式。

标签:flappy,void,unity,bird,管道,预制,lastTime,我们,public
来源: https://www.cnblogs.com/ying-mei/p/16031235.html