第一个Unity项目——Flappy Bird

Unity是什么:
它就是一个2D/3D编辑器,同时又是一个游戏引擎
游戏引擎:使用者只需要调用这些功能,而不需要重写实现这些功能

第一步:资源导入与资源处理

资源导入的方法:

  1. 第一个Unity项目——Flappy Bird_第1张图片

  2. 直接将资源拖入Project面板

资源处理
2D游戏总的来说是依靠Sprites(图片精灵)

第一个Unity项目——Flappy Bird_第2张图片
Sprite的类型
Default——默认纹理类型
Normal map——法线贴图资源
Editor GUI and Legacy ——用户界面的特图资源
Sprite——图片精灵
Cursor——鼠标光标
Cookie——光斑
Lightmap——光照贴图
Directional Lightmap——方向光贴图
Single Channel——单通道纹理

主角图片的创建
第一个Unity项目——Flappy Bird_第3张图片
首先要将Single(单张图片) 变为Multiple(多张图片)
然后点击Sprite Editor 进行图片编辑
第一个Unity项目——Flappy Bird_第4张图片
对图片进行分割
Automatic 自动
Grid By Cell Size 按照每一格像素切割
Gird By Cell Count 按照图片的数量切割

在这里插入图片描述
透明模式与颜色模式

场景创建

第一个Unity项目——Flappy Bird_第5张图片
场景的层级关系
不同的图片处于不同的层级
Sorting Layer 层级的设置
Order in Layer 层级的优先级
1、
同一层级的图片,优先级越大,图片越处在前面

2、
第一个Unity项目——Flappy Bird_第6张图片
添加新的图层,越在下的图层就越在前。

项目开始:快捷Ctrl+P

主角组件创建

在这里插入图片描述
增加组件

第一个Unity项目——Flappy Bird_第7张图片
2D物理组件
第一个Unity项目——Flappy Bird_第8张图片
2D刚体

第一个Unity项目——Flappy Bird_第9张图片
圆形碰撞器

场景组件创建

第一个Unity项目——Flappy Bird_第10张图片
添加Colider(碰撞器)

在这里插入图片描述
编辑编辑器

第二步:添加代码脚本

设置代码编辑器:
第一个Unity项目——Flappy Bird_第11张图片

第一个Unity项目——Flappy Bird_第12张图片
常见的MonoBehaviour类的消息
第一个Unity项目——Flappy Bird_第13张图片
快捷使用unity的各类方法(消息)
第一个Unity项目——Flappy Bird_第14张图片
第一个Unity项目——Flappy Bird_第15张图片

  1. 点击屏幕时,使Bird能够飞翔
  if (Input.GetMouseButtonUp(0))//Input为unity的一个类,调用这个类中的GetMouseButtonUp()的方法 ,检查鼠标是否按下。(0位左键,1为右键)
            {
                rb2d.velocity = Vector2.zero;//将速度设置为0,每一帧的速度都为0

                rb2d.AddForce(new Vector2(0, 360));//添加一个向上的力
            }

2.小鸟碰撞与死亡

   bool isDead=false;
    private void OnCollisionEnter2D(Collision2D collision)//碰撞检测
    {
        isDead = true;

    }

碰撞有三种状态
1、OnCollisionStay2D 碰撞进行
2、OnCollisionEnter2D 碰撞开始
3、OnCollisionExit2D 碰撞退出

思考题
1、Unity引擎是如何知道我们的脚本中没有写特定名字的方法的?又是如何调用的?
通过反射,查找特定的名字
通过反射直接调用该方法
2、能否将碰撞处理放在Ground对象上?

   private void OnCollisionEnter2D(Collision2D collision)
    {
        //collision.gameObject.GetComponent().enabled = false;//获得碰撞体的SpriteRenderer组件,并启用状态设为关闭

        collision.gameObject.GetComponent<Bird>().isDead = true;//将碰撞体的Bird组件的isDead设为ture
    }

3、一个对象A的子对象B发生1碰撞,A会不会收到碰撞信息

1、如果父节点和子节点都有Rigidbody,就相当于两个独立的物体,各自碰各自的。
2、如果父节点有Rigidbody,子节点没有,子节点相当于父节点的一部分,就看在谁的身上有响应的接口,在父节点有响应接口,父节点响应,子节点有接口,子节点响应,两者都有接口,父节点不会影响到子节点,但子节点响应会让父节点也响应。
3、如果父节点没有Rigidbody,子节点有,子节点会响应,父节点没有响应。

GameObject 和gameobject的区别

GameObject是Unity场景中所有实体的基类,是一个类型
gameobject是一个对象,就和this一样,指这个脚本所附着的游戏物体

第三步 动画创建

unity中的动画系统

1、选中游戏对象
第一个Unity项目——Flappy Bird_第16张图片
2、创建动画并命名
第一个Unity项目——Flappy Bird_第17张图片

第一个Unity项目——Flappy Bird_第18张图片
动画控制器
第一个Unity项目——Flappy Bird_第19张图片
游戏对象上Animator都会有一个动画控制器

在这里插入图片描述
动画片段

3、动画制作
打开动画片段后,选中游戏对象

进入动画编辑界面
第一个Unity项目——Flappy Bird_第20张图片
开启记录关键帧,将图片拖入动画,将采样率设置为1,。
定位到第一秒中的位置
添加关键帧(keyframe)
最后停止记录关键帧

4、动画控制器设置
进入动画控制器中,建立各个动画之间的转换
第一个Unity项目——Flappy Bird_第21张图片
添加两个触发器
在这里插入图片描述
在Idle转换Flap中,将触发器设置为转换的条件
Conditions:条件
第一个Unity项目——Flappy Bird_第22张图片
然后通过代码进行控制

   Animator anim;
 anim = GetComponent<Animator>();
   anim.SetTrigger("Flap");

完成后发现动画播放比较慢,将
在这里插入图片描述
取消。
它的作用就是是否等待上一个动画播放结束。

将Idle与Flap相互联系,可以让Idle和Flap相互转换,但注意在Flap转换Idle时,要将Has Exit Time 勾上,以防止在长时间没有Flap下,可以让Flap自动转换为Idle。
第一个Unity项目——Flappy Bird_第23张图片

第四步 UI设计

添加三个Text
分别是 Score、Gameover、HinText
用于显示分数、游戏结束、游戏提示

在这里插入图片描述

然后进行UI设计

第一个Unity项目——Flappy Bird_第24张图片

Text:文本内容设置
Font :文本字体设置
Font Size :文本字体大小设置
Horizontal Overflow :水平溢出
Vertical Overflow:垂直溢出
Color:颜色

第五步:游戏逻辑控制器

添加一个空对象作为游戏逻辑控制器
在这里插入图片描述
然后增加一个游戏逻辑控制脚本

public class GameControl : MonoBehaviour
{
    bool gameOver = false;
    public GameObject gameovertext;//设置一个Hierarchy层的游戏对象
    public GameObject hintext;
    public GameObject scoretext;

    private GameControl()
    { }

    private static GameControl instance ;//游戏控制器一般只有一个,所以使用单例模式

    public static GameControl GetInstance()
    {
        if (instance == null)
        {
            instance = new GameControl();

        }
        return instance;
    }

    private void Awake()
    {
        instance = this;
    }

    void Start()
    {
        
    }

    void Update()
    {
        if (gameOver==true && Input.GetMouseButtonUp(0))
        {
            SceneManager.LoadScene("Main"); //加载场景
        }
        
    }

   public void  OnBirdDied()
    {
        gameOver = true;
        gameovertext.SetActive(true);//死亡后使Text显示处理
        hintext.SetActive(true);
    }
 
}

碰撞时调用上面的OnBirdDied()方法
Brid类
  private void OnCollisionEnter2D(Collision2D collision)//碰撞检测
    {
        isDead = true;//优先更改数据,再进行画面表现
        anim.SetTrigger("Die");
        GameControl.GetInstance().OnBirdDied();//调用方法

    }

同时注意要将代码中的字段与游戏物体相关联。
第一个Unity项目——Flappy Bird_第25张图片

第六步 制作卷轴背景

方法一:通过动画实现
添加两个动画控制器,分别控制两个不同的背景进行滚动
第一个Unity项目——Flappy Bird_第26张图片
第一个Unity项目——Flappy Bird_第27张图片
注意动画播放的速度要改为匀速

方法二:通过代码控制

public class Scrolling : MonoBehaviour
{
    Rigidbody2D rb2d;
    float startPosx;//起始的位置
    float width;

    void Start()
    {
        rb2d = GetComponent<Rigidbody2D>();
        rb2d.velocity = new Vector2(-5, 0);
        width = GetComponent<BoxCollider2D>().size.x;
        startPosx = transform.position.x;


    }


    void Update()
    {
        if (GameControl.GetInstance().gameOver == true)
        {
            rb2d.velocity = Vector2.zero;//将卷轴停下
     
            return;
        }


        if (startPosx - transform.position.x >= width)//如果起始位置-现在的位置
        {
            Vector3 pos = transform.position;//trsnsfrom作为结构体,position属性无法直接赋值
            pos.x = startPosx;
            transform.position = pos;
        }


    }
}

第七步 添加障碍

第一个Unity项目——Flappy Bird_第28张图片
将障碍设置为预制体,然后给障碍添加滚动的脚本。

第一个Unity项目——Flappy Bird_第29张图片
在原有的scrolling脚本上修改。

public 让外界可以控制障碍回头的距离,方便与其他有相同的游戏物体区分。
if(width==0) 是让与障碍相同的脚本的游戏物体可以共用脚本。


注意:
要将刚体组件设置为Kinematic.,让游戏物体不受物理影响

在这里插入图片描述

第八步 通过障碍加分

第一个Unity项目——Flappy Bird_第30张图片

在预制体中,给障碍添加一个得分的碰撞器,然后将碰撞器改为 Trigger触发器的模式

代码

Control类

  public GameObject scoretext;
    int score = 0;
  public void OnScore()
    {
        score += 10;
        scoretext.GetComponent<Text>().text = string.Format("Score:{0}", score);
    }

Bird类

  private void OnTriggerExit2D(Collider2D collision)//触发检测
    {
        GameControl.GetInstance().OnScore();
    }

第九步 动态生成障碍

第一个Unity项目——Flappy Bird_第31张图片
添加两个空节点Upper 和Lower 作为最小上界 和最小下界

代码

public class Spawner : MonoBehaviour
{//Spawner 生成者

    public GameObject gamePrefab;
    public GameObject lowerbound;
    public GameObject upperbound;

    public float interval = 4f; //时间间隔
    void Start()
    {
        InvokeRepeating("Spawn",interval,interval);//Invoke:调用 Repeating:重复 //4秒已后,每隔4秒,重复执行“Spawner”方法
        
    }
    void Spawn()
    {
        //GameObject column = new GameObject();
        //column = gamePrefab;

       GameObject column=  Instantiate(gamePrefab);//根据预制体实例化一个对象
        column.transform.position = Vector3.Lerp(lowerbound.transform.position,upperbound.transform.position,Random.Range(0f,1f));

        
    }


    void Update()
    {
        
    }
}

定点消除障碍
Scrolling类

  public bool destory = false;
    if (startPosx - transform.position.x >= width)//如果起始位置-现在的位置
        {
            if (destory == true)//将destory勾上,就可以执行到这
            {
                Destroy(gameObject);
            }
            else
            {
            Vector3 pos = transform.position;//trsnsfrom作为结构体,position属性无法直接赋值
            pos.x = startPosx;
            transform.position = pos;
            }
            
          
        }

注意在预制体中
在这里插入图片描述
因为要执行到destory。

补充 对象池的使用

动态生成障碍,会造成内存碎片。

代码

ColumnPool类

public class ColumnsPool : MonoBehaviour
{
   public int poolsize = 100;

    public GameObject gamePrefab;
    public GameObject lowerbound;
    public GameObject upperbound;
    public float interval = 4f; //时间间隔

    public Queue<GameObject> pools;

    public static ColumnsPool instance;

    private void Awake()
    {
        instance = this;
    }


    void Start()
    {
        pools = new Queue<GameObject>();
        for (int i = 0; i < poolsize; i++)//实例化一百个柱子,将其设为关闭。然后入队
        {
            GameObject column = Instantiate(gamePrefab);
            column.SetActive(false);
            pools.Enqueue(column);
        }
        InvokeRepeating("Spawn", interval, interval);

    }

    void Spawn()//栈内的柱子预制体出队,将其启动,然后柱子在上界和下界取随机值,生成不同的柱子。
    {
        GameObject column = pools.Dequeue();
        column.SetActive(true);
        column.transform.position = Vector3.Lerp(lowerbound.transform.position, upperbound.transform.position, Random.Range(0f, 1f));

    }
    public void Despawn(GameObject go)//将柱子关闭并重新入队
    {
        go.SetActive(false);
        pools.Enqueue(go);
    }

}

Scrolling类调用Despawn方法

   ColumnsPool.instance.Despawn(this.gameObject);

对象池的好处:在生成一个固定的游戏物体时,可以减少不断重复实例化带来的内存碎片。
实质:利用队列先进先出的特性,使障碍一直保持固定的数量,不增加内存的消耗。

你可能感兴趣的:(unity)