用Unity3D开发2D小游戏 Flappy Bird

时间:2021-07-17 11:46:22

简介:

最近在学习Unity3D,用了两天时间做了个小游戏打算放上了和大家分享一下,项目名定义为Flapping,是参考Flappy Bird做的,高手勿喷。


这是原本游戏效果图:

用Unity3D开发2D小游戏 Flappy Bird


这是本项目效果图:

用Unity3D开发2D小游戏 Flappy Bird


资源下载:

1. 完整源代码下载

2. PC发布版下载

3. Android发布版APK下载


源代码:

源代码里已经打好了注释,主要分为3个C#脚本。第一个是Player.cs,是本游戏最核心脚本,用来初始化场景和控制小鸟;第二个是Pillar.cs,处理柱子被小鸟撞后的特效;第三个是Menu.cs,这是菜单。


Player.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class Player : MonoBehaviour {

// Use this for initialization
void Start () {
// 获取各个对象或组件
goBird = transform.Find ("Bird").gameObject;
srBird = goBird.GetComponent<SpriteRenderer>();
goCamera = GameObject.Find ("Main Camera");
goScores = GameObject.Find ("Scores");

if (canPlay) {
InitBackground ();
InitBlocks ();
// 重设分数
PlayerPrefs.SetInt ("scores", 0);
} else {
// 显示分数
if (goScores) {
goScores.transform.GetComponent <Text>().text = "SCORES: "+PlayerPrefs.GetInt("scores");
}
}
}

public bool canPlay = true; // 判断是否能开始
public float screenHeight = 10; // 屏幕高度
public float moveSpeed = 5; // 小鸟移动速度
public float upSpeed = 19; // 往上爬速度
public float upHeight = 0.9f; // 往上爬高度
private float curUpHeight = 0; // 当前高度
private bool upping = false; // 判断是否正在攀爬
public float flapInterval = 0.1f; // 多长时间需要改变小鸟的样子
private float curInterval = 0; // 当前经过的时间
public Sprite srUnflap; // 小鸟正常样子
public Sprite srFlapped; // 小鸟正在攀爬的样子
private GameObject goBird; // 小鸟对象
private SpriteRenderer srBird; // 存放小鸟样子
private GameObject goCamera; // 摄像头
private bool isDead = false; // 判断是否死亡
private GameObject goScores; // 分数对象
private int scores = 0; // 分数
public AudioClip collisionAduio; // 碰撞音效

// Update is called once per frame
void Update () {
// 判断是否死亡
if (!isDead) {
UpdateFlap ();

// 如果没死就继续更新下一帧
if (canPlay) {
// 更新动作
UpdateMovement ();
// 更新分数
UpdateScores ();
// 更新会动的柱子
UpdateMovingBlocks ();
}
} else {
UpdateDieCompleted ();
}
}

private void UpdateMovement() {
float s = Time.deltaTime * moveSpeed;
transform.Translate (new Vector3 (s, 0, 0));

// 重设摄像头位置
goCamera.transform.position =
new Vector3(transform.position.x,goCamera.transform.position.y,goCamera.transform.position.z);

if ((Input.GetKeyUp (KeyCode.Space) || Input.GetButtonUp("Fire1"))) {
curUpHeight = 0;
upping = true;

// 旋转小鸟的角度为45度
goBird.transform.eulerAngles = new Vector3 (0,0,45);
}

// 往上爬
if (upping) {
float us = upSpeed * Time.deltaTime;
if (us + curUpHeight >= upHeight) {
us = upHeight - curUpHeight;
}
transform.Translate (new Vector3(0,us,0));

curUpHeight += us;
if (curUpHeight >= upHeight) {
upping = false;
curUpHeight = 0;

// 旋转小鸟的角度为正常
goBird.transform.eulerAngles = new Vector3 (0,0,0);
}
}

// 如果撞到天花板就往下坠
float top = transform.localScale.y / 2 + transform.position.y;
if (top >= screenHeight / 2) {
upping = false;
curUpHeight = 0;

// 旋转小鸟的角度为正常
goBird.transform.eulerAngles = new Vector3 (0,0,0);
}

// 如果碰到地板就死亡
float bottom = -transform.localScale.y / 2 + transform.position.y;
if (bottom <= -screenHeight / 2) {
// 小鸟死亡
BirdDie ();
}
}

private void BirdDie() {
isDead = true;
goBird.transform.eulerAngles = new Vector3 (0,0,90);
}

private void UpdateDieCompleted() {
// 如果碰到地板就死亡
float bottom = transform.localScale.y / 2 + transform.position.y;
if (bottom <= -screenHeight / 2) {
DieCompleted ();
}
}

private void DieCompleted() {
Destroy (gameObject);
// 载入Gameover场景
SceneManager.LoadScene ("GameOver");
SceneManager.UnloadSceneAsync ("Game_1");
}

// 改变小鸟样子
private void UpdateFlap() {
curInterval += Time.deltaTime;
if (curInterval >= flapInterval && !upping) {
if (srBird.sprite == srUnflap) {
srBird.sprite = srFlapped;
} else {
srBird.sprite = srUnflap;
}
curInterval = 0;
}
}

// 更新分数
private void UpdateScores() {
float tail = transform.position.x - transform.localScale.x / 2 - blockWidth;
tail = (tail < 0) ? 0 : tail;
int killBlocks = (int)(tail / (blockOffset) + ((tail > blockWidth) ? 1 : 0));
scores = killBlocks * 10;
goScores.transform.GetComponent <Text>().text = "SCORES: "+scores;

// 保存分数
PlayerPrefs.SetInt ("scores",scores);

// 判断游戏是否已经结束
if (scores >= (totalBlock * 10)) {
DieCompleted ();
}
}

// 柱子
public GameObject block;
public float blockWidth = 1; //柱子宽度
public float blockMinHeight = 1; // 柱子最小高度
public float blockOffset = 8; // 柱子距离
public float blockExitHeight = 4; // 两柱之间给小鸟通过的缝的距离
public float blockNarrow = 0.2f; // 两柱之间给小鸟通过的缝的距离的递减数,越后的柱子的缝越小
public int totalBlock = 40; // 柱子数量
public int blockClass = 5; // 将所有柱子分为多少组
private List<GameObject> movingBlocks = new List<GameObject>(); // 会移动的柱子
private float curBlockMoveHeight = 0; // 柱子移动高度
public float blockMoveSpeed = 1; // 柱子移动速度
private bool isBlockMovingUp= true; // 判断是否正在移动

private GameObject CreateBlock(Vector3 pos, Vector3 scale, Vector3 rotation) {
GameObject go = GameObject.Instantiate (block).gameObject;
go.transform.position = pos;
go.transform.localScale = scale;
go.transform.Rotate(rotation);
return go;
}

// 初始化柱子
private void InitBlocks() {
int perClassBlock = totalBlock / blockClass;
float x = 0.0f;
System.Random rm = new System.Random (); // 随机数

for (int i = 0; i < blockClass; ++i) {
// 越后的柱子的缝越窄,这就表示游戏越难
float narrow = blockExitHeight - i*blockNarrow;
float avaliableHeight = screenHeight - narrow;

for (int j = 0; j < perClassBlock; ++j, x+=blockOffset) {
float height1,height2,y1,y2;
height1 = (float)rm.NextDouble() * avaliableHeight;
float blockMinHeight2 = blockMinHeight * 2;
height1 = (height1 > (avaliableHeight - blockMinHeight2)) ? (avaliableHeight - blockMinHeight2) : height1;
height1 = (height1 < blockMinHeight2) ? (blockMinHeight2) : height1;
height2 = avaliableHeight - height1;
y1 = (screenHeight - height1) / 2;
y2 = (screenHeight - height2) / 2;

GameObject go1 = CreateBlock (new Vector3(x,y1), new Vector3(1,height1/2,1), new Vector3(0,0,0));
GameObject go2 = CreateBlock (new Vector3(x,-y2), new Vector3(1,height2/2,1), new Vector3(0,0,180));

// 保存会动的柱子
if (j >= perClassBlock - 2) {
movingBlocks.Add (go1);
movingBlocks.Add (go2);
}
}
}
}

// 移动会动的柱子
private void UpdateMovingBlocks() {
float speed = blockMoveSpeed*Time.deltaTime;

if (speed + curBlockMoveHeight >= blockMinHeight) {
speed = blockMinHeight - curBlockMoveHeight;
}

curBlockMoveHeight += speed;
speed = isBlockMovingUp ? speed : -speed;

for (int i = 0; i < movingBlocks.Count; i += 2) {
int j = i + 1;

Vector3 pos1 = movingBlocks[i].transform.position;
Vector3 pos2 = movingBlocks[j].transform.position;
Vector3 scale1 = movingBlocks [i].transform.localScale;
Vector3 scale2 = movingBlocks [j].transform.localScale;

scale1.y -= speed;
scale2.y += speed;

pos1.y = (screenHeight - scale1.y*2)/2;
pos2.y = -(screenHeight - scale2.y*2)/2;

movingBlocks [i].transform.position = pos1;
movingBlocks [j].transform.position = pos2;
movingBlocks [i].transform.localScale = scale1;
movingBlocks [j].transform.localScale = scale2;
}

isBlockMovingUp = curBlockMoveHeight >= blockMinHeight ? !isBlockMovingUp : isBlockMovingUp;
curBlockMoveHeight = curBlockMoveHeight >= blockMinHeight ? 0 : curBlockMoveHeight;
}

// 背景
public GameObject background;
public float bgWidth = 20;
public float bgStartPos = -10;
public int bgCount = 50;

// 创建背景
private void CreateBackground(Vector3 pos, Vector3 scale) {
GameObject go = GameObject.Instantiate (background).gameObject;
go.transform.position = pos;
go.transform.localScale = scale;
}

// 初始化背景
private void InitBackground() {
int n = bgCount;
for (int i=0; i<n; ++i) {
CreateBackground(new Vector3(bgStartPos+i*bgWidth,0,2),new Vector3(1,1,1));
}
}

// 小鸟的碰撞处理
void OnCollisionEnter2D(Collision2D col) {
if (col.gameObject.tag == "Block") {
AudioSource a = GetComponent<AudioSource>();
if (!isDead) {
BirdDie ();

// 发出碰撞声音
a.clip = collisionAduio;
a.Stop ();
}

a.Play ();
}
}
}


Pillar.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Pillar : MonoBehaviour {

// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update () {

}

public int crushPiece = 5; // 碎片数量
public float pieceWidth = 0.8f; // 碎片宽高
public GameObject piece; // 碎片对象

// 碰撞后弹出碎片
private void Crush(Collision2D col) {
System.Random rm = new System.Random ();
for (int i = 0; i < crushPiece; ++i) {
Vector3 pos = new Vector3(col.contacts [0].point.x, col.contacts [0].point.y, 0);
Vector3 scale = new Vector3 (pieceWidth,pieceWidth, 1);
float fr = (float)rm.NextDouble ();
float angle = (float)fr * -180;
Vector3 rotation = new Vector3 (0,0,angle);

GameObject go = CreatePiece (pos,scale,rotation);
go.transform.GetComponent<Rigidbody2D>().AddForce(new Vector2(-fr*2,3) * 100);
Destroy (go, 3.0f);
}
}

private GameObject CreatePiece(Vector3 pos, Vector3 scale, Vector3 rotate) {
GameObject go = GameObject.Instantiate (piece);
go.transform.position = pos;
go.transform.localScale = scale;
go.transform.eulerAngles = rotate;
return go;
}

void OnCollisionEnter2D(Collision2D col) {
if (col.gameObject.tag == "Player") {
Crush (col);
}
}
}


Menu.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class Menu : MonoBehaviour {
public void StartGame() {
// 载入游戏
SceneManager.LoadScene ("Game_1");
SceneManager.UnloadSceneAsync ("Menu");
SceneManager.UnloadSceneAsync ("GameOver");
}

public void QuitGame() {
// 退出游戏
Application.Quit ();
}
}