U2D愤怒的小鸟学习笔记

# 官方文档:https://docs.unity3d.com/ScriptReference/index.html

Part 1

基本操作
1.左下角Project面板中Assets下创建Image,Music,Scenes,Script四个文件夹,Packages不用管
2.在Scenes文件夹下新建几个unity场景文件,把预先备好的素材拉入Image与Music
3.选中Image下第一张图,例如BIRDS_1,右边Inspector面板中设置Sprite Mode为Multiple,点sprite Editor,在新窗口中点Slice并保存
4.回来Image文件夹下会看到BIRDS_1可以再细分许多子图并且已经自动命名
5.其他图片一样操作,至此素材导入完成
6.点击对应的Scene,把所需图片拉到左上方Hierarchy面板下,每个Scene都会自带一个Main Camera
7.在中间上方的Scene面板中摆放图片与摄像机的位置,操作方法跟小学时学的3DMAX差不多
8.点击正上方运行按钮,在正下方的Game面板中观察摄像机的映像,当然此时未设置动画
9.最后就是不断给场景中的对象在右边Inspector面板中add component,完

Part 2

Inspector面板
1、可见,对象ID(对应代码),静态,Tag,Layer(新建一个层把用到的素材全部拉进去)
2、Transform是本对象的资态:世界XYZ绝对坐标,俯仰角/偏航角/翻滚角,XYZ缩放比例
3、Sprite Renderer:Sprite设置素材源,Order in Layer可以设置在本Layer中的等级,值越大越靠前显示
4、其他的Component就需要点击最下面的Add Component添加,添加脚本是直接输入脚本名确认,建议是脚本名与对象名能够对应
5、以下面的代码为例,有四个响应事件的API,还有三个局部变量,凡是设成public的变量可以在Inspector面板中直接更改他的值
//Bird.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Brid : MonoBehaviour{
    private bool isClick=false;
    public Transform rightPos;
    public float maxDis=3;
    private void OnMouseDown(){//对本对象鼠标按下左键时的事件
        isClick=true;
    }
    private void OnMouseUp(){//对本对象鼠标释放左键时的事件
        isClick=false;
    }
    private void Update(){//本对象每帧显示前的事件
        if(isClick){
            transform.position=Camera.main.ScreenToWorldPoint(Input.mousePosition);//重点!
            transform.position+=new Vector3(0,0,-Camera.main.transform.position.z);
            if(Vector3.Distance(transform.position,rightPos.position)>maxDis){
                Vector3 pos=(transform.position-rightPos.position).normalized;//方向不变长度变成1
                pos*=maxDis;//让向量乘上最大距离得到相对于基点的向量
                transform.position=pos+rightPos.position;//偏移向量+基点向量
            }
        }
    }
    private void Start(){//本对象第一帧显示前的事件
        //可以设置初始姿态什么的
    }
}
//这里要对上面的“重点”解释一下:
//屏幕按下对象有Input.mousePosition,然后转换成世界坐标得到transform.position,transform就是本对象
//2D场景平行视角摄像机默认坐标是(0,0,-10),摄像头面向Z轴正向,坐标转换后的坐标是落在摄像头所在摄影平面的!
//摄像机对用户触摸屏幕点坐标的转换时XY坐标可以正确转换,但是Z坐标会变成取摄像机的坐标,所以z坐标要减去摄像机z坐标

Part 3

Rigidbody 2D:当Boty Type为Dynamic时绑定动力学(如自由落体等)引擎,例如小鸟,angular drag是滚动摩擦系数
Rigidbody 2D:当Boty Type为Kinematic时绑定不受动力学引擎,例如用户拖动小鸟时
(当然用户拖动时用Dynamic也OK,反正脚本中重设了对象坐标,但是这样释放时飞出去的速度会很大)
Rigidbody 2D:当Boty Type为Static时绑定可以作为Spring Joint 2D的Connected Rigid Body参数的值(弹簧另一端),例如树枝
Spring Joint 2D:绑定弹簧关节引擎,Connected Rigit Body参数是弹簧的另一端,Frequnecy是阻尼系数
Circle Collider 2D:绑定圆形碰撞引擎,Radius参数是响应半径
Box Collider 2D:绑定矩形碰撞引擎,Edit Collider可以设置响应边框

Part 4 小鸟飞出

private SpringJoint2D sp;
private Rigidbody2D rg;
private void Awake(){
    sp=GetComponent<SpringJoint2D>();
    rg=GetComponent<Rigidbody2D>();
}
private void OnMouseDown(){
    isClick=true;
    rg.isKinematic=true;//不受动力学控制,可以让用户自由拖动
}
private void OnMouseUp(){
    isClick=false;
    rg.isKinematic=false;//受动力学控制,计算受力改变其速度
    Invoke("Fly",0.1f);//计算受重力与弹簧力0.1秒之后执行Fly函数
}
private void Fly(){
    sp.enabled=false;//弹簧失效,注意Frequency的值是阻尼系数
}

Part 5 场景

对THEME_01_THEME_1中进行slice得到地面拉到场景中
Add Componenet-Box Collider 2D-Edit Collider把碰撞框上边线往下拉一点让鸟在草上滚
新建env文件夹把THEME_01_THEME_1_0放进去,ctrl+d复制多份并在Scene面板中往右拉铺满整个场景
然后再把小草与天空放上来即可

Part 6 猪

//BIRDS_1_103拉进去场景并更改ID为pig
//添加Circle Collider 2D,按Edit Collider调整大小
//添加Rigidbody 2D,调整 Angular Drag为2
//添加Add Component,添加下面代码
public class Pig : MonoBehaviour{
    public float maxSpeed=5.0f;
    public float minSpeed=3.0f;
    private SpriteRenderer render;//精灵渲染类对象声明
    public Sprite hurt;
    private void Awake(){
        render=GetComponent<SpriteRenderer>();//定义对象为绑定的本对象
    }
    private void OnCollisionEnter2D(Collision2D collision){//碰撞检测
        print(collision.relativeVelocity.magnitude);
        if(collision.relativeVelocity.magnitude<minSpeed){
            return ;
        }
        else if(collision.relativeVelocity.magnitude>maxSpeed){
            Destroy(gameObject);
        }else if(collision.relativeVelocity.magnitude<maxSpeed && collision.relativeVelocity.magnitude>minSpeed){
            render.sprite=hurt;
        }
    }
}
//回到Inspector面板中调整上面三个public参数,hurt的值是BIRDS_1_104图片

part 7 弹弓

//左树枝left_branch加Line Renderer并设材质,在其下创建空对象并更名为leftPos
//右树枝right_branch加Line Renderer并设材质,在其下创建空对象并更名为rightPos(前面画弹簧建了可跳过)
//在Bird类中加入四个public成员,并在Inspector面板中拖动赋值
public LineRenderer right;//这四变量是树枝对象的子类对象,要在Inspector面板中赋值
public LineRenderer left;
public Transform leftPos;
public Transform rightPos;
//在update函数处加入四行代码
right.SetPosition(0,rightPos.position);
right.SetPosition(1,transform.position);
left.SetPosition(0,leftPos.position);
left.SetPosition(1,transform.position);

part 8 死亡特效

拖死亡动画第一帧到场景,重命名为Boom
增加Boom.cs脚本
public class Boom : MonoBehaviour{
    public void destroying(){
        Destroy(gameObject);
    }
}
增加Animator,ctrl+6打开动画编缉器,按create创建一个动画,放到Assets下的Animation下(无就新建)
依次拖入关键帧,并在最后一帧添加事件destroying,把场景中的Boom拖动到Assets下的prefabs下(无就新建)
在Pig.cs中,猪destroy函数后加上Instantiate(boom,transform.position,Quaternion.identity);
类成员中加上public GameObject boom;,回到Inspector面板把prefabs下的Boom拉到脚本赋值处

part 9 分数特效

拖分数到场景重命名为pigScore,再拖动到prefabs下,在Pig.cs中,在上面给死亡动画Instantiate下再写:
GameObject go=Instantiate(score,transform.position+new Vector3(0,0.8f,0),Quaternion.identity);
Destroy(go,1.5f);
在声明类成员变量时写public GameObject score;,回到Inspector面板把prefabs下的pigScore拉到脚本赋值处

part 10 游戏管理(重难点)

//创建新的空类GameManager,往GameManager.cs中写入
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour{
    public List<Bird> birds;
    public List<Pig> pigs;
    public static GameManager _instance;
    private void Awake(){
        _instance=this;
    }
    private void Start(){
        Initialized();
    }
    private void Initialized(){
        for(int i=0;i<birds.Count;i++){
            if(i==0){
                birds[i].enabled=true;
                birds[i].sp.enabled=true;
                //birds[i].cc.enabled=true;
            }else{
                birds[i].enabled=false;
                birds[i].sp.enabled=false;
                //birds[i].cc.enabled=false;
            }
        }
    }
    public void NextBird(){
        if(pigs.Count>0){
            if(birds.Count>0){
                Initialized();
            }else{
                print("lose"); 
            }
        }else{
            print("win");
        }
    }
}
//往inspector面板中按顺便拖入鸟与猪
//Bird.cs中设置在Fly()后触发Next()唤醒下一只鸟
private void Fly(){
    sp.enabled=false;//弹簧失效,注意Frequency的值是阻尼系数
    Invoke("Next",5);
}
private void Next(){
    GameManager._instance.birds.Remove(this);
    Destroy(gameObject);
    Instantiate(boom,transform.position,Quaternion.identity);
    GameManager._instance.NextBird();
}
//至此游戏主体实现完成!!!

part 11 上树

GameManager.cs的Awake中加上originPos=birds[0].transform.position;
GameManager.cs的Initialized中加上birds[0].transform.position=originPos;
Bird.cs的OnMouseUp末加上right.enabled=false;left.enabled=false;

part 12 弹弓

Bird.cs的Update中SetPosition前加上right.enabled=true;left.enabled=true;

part 13 木块

1. 插入多种木块图
2. 创建Rigidbody 2D使用物理学引擎如自由落体
3. 创建Box Collider 2D使用盒式碰撞引擎
4. 使用pid脚本并赋hurt/score/ispid参数
5. copy多份

part 14 胜负界面

在Hierarchy界面下创建UI-Image重命名为lose,是全屏黑,不透明度一半
在lose下创建UI-Image重命名为menu,是左1/3屏黑,不透明度0
在menu创建UI-Image重命名为retry,插图是重玩按扭图片
在menu创建UI-Image重命名为exit,插图是退出按扭图片
(千万不要拉到prefabs下!)
在Hierarchy界面下创建UI-Image重命名为win,是全屏黑,不透明度一半
在lose下创建UI-Image重命名为menu,是左1/3屏黑,不透明度0
在menu创建UI-Image重命名为next,插图是下一关按扭图片
在menu创建UI-Image重命名为exit,插图是退出按扭图片
(千万不要拉到prefabs下!)
修改GameManager.cs
声明变量public GameObject Win;并把lose拉过来属性面板赋值
声明变量public GameObject Loss;并把lose拉过来属性面板赋值
在输出失败后加上Loss.SetActive(true);
在输出成功后加上Win.SetActive(true);
(补充说明下layer与sorting layer:)
sorting layer用于渲染,Unity渲染关系层级顺序:Camera -> sorting layer -> sorting order
layer用于碰撞检测,光线投射

part 15 按钮事件(重玩/下一关/退出)

File-Build Setting-把全部unity场景文件拉上去
在GameManager.cs中写三个函数
public void Next(){SceneManager.LoadScene(3);}
public void Replay(){SceneManager.LoadScene(2);}
public void Home(){SceneManager.LoadScene(1);}
给三个按钮Add Component-OnClick,把GameManager拉过来,再选对应的函数

part 16 镜头跟随

改摄像机的x坐标,涉及的接口:
static function Lerp (from : Vector3, to : Vector3, t : float) : Vector3 
---->返回值=from+(to-from)*t
Mathf.Clamp(float value,float min,float max)
---->在 Mathf.Clamp 中传入三个参数:value,min,max,限制 value的值在min,max之间,如果value大于max,则返回max,如果value小于min,则返回min,否者返回value;
Time.deltaTime
---->每帧间隔时间,如20帧每秒,即0.05秒/帧,则Time.deltaTime=0.05
写法一:Camera.main.transform.position=new Vector3(Mathf.Clamp(posX,0,15),Camera.main.transform.position.y,Camera.main.transform.position.z);
缺点:跟随可以实现,但是换鸟是会突然转变
写法二:float posX=transform.position.x;
Camera.main.transform.position=Vector3.Lerp(Camera.main.transform.position,\
                                            new Vector3(Mathf.Clamp(posX,0,15),\
                                                        Camera.main.transform.position.y,\
                                                        Camera.main.transform.position.z),\
                                            3*Time.deltaTime);
这个函数的意义:相机初始坐标0,0,-10,小鸟坐标的x在大于0时,返回值=from+(to-from)*t!=from产生跟随,且t是3*Time.deltaTime小于1所以不是即时跟随,平滑换鸟
3这个值的意义是1秒移动3米,太小的话相机跟不上,太大的话换鸟时会突然变化镜头位置,要自己调参得出

part 17 加载音乐

public AudioClip birdCollision;
if(collision.gameObject.tag=="Player")AudioSource.PlayClipAtPoint(birdCollision,transform.position);

part 18 黄鸟

//换贴图
//写脚本Bird_yellow.cs继承
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bird_yellow : Bird{
    private bool fly_again;
    private new void Awake(){
        base.Awake();
        fly_again=false;
    }
    private new void OnMouseDown(){
        if(isFlying){
            if(!fly_again){
                rg.velocity*=2;
                fly_again=true;
            }
            return ;
        }
        if(!isClick)AudioSource.PlayClipAtPoint(select,transform.position);
        isClick=true;
        rg.isKinematic=true;
    }
}
//在面板重新赋值,GameManager也要赋
//测试参数

part 19 绿鸟

//换贴图
//写脚本Bird_green.cs继承
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bird_green : Bird{
    private bool fly_again;
    private new void Awake(){
        base.Awake();
        fly_again=false;
    }
    private new void OnMouseDown(){
        if(isFlying){
            if(!fly_again){
                Vector3 speed=rg.velocity;
                speed.x*=-1;
                rg.velocity=speed;
                fly_again=true;
            }
            return ;
        }
        if(!isClick)AudioSource.PlayClipAtPoint(select,transform.position);
        isClick=true;
        rg.isKinematic=true;
    }
}

//在面板重新赋值,GameManager也要赋
//测试参数

part 20 开始菜单

开场场景:插背景图,插UI-Button(进入游戏按钮与退出游戏按钮),绑定下面写的两个方法
创建GameManager对象写GameManager.cs

public class StartMenuManager : MonoBehaviour{
    public void Enter(){
        //print("enter");
        UnityEngine.SceneManagement.SceneManager.LoadScene(1);
    }
    public void Exit(){
        //print("exit");
        #if UNITY_EDITOR
            UnityEditor.EditorApplication.isPlaying = false;
        #else
            Application.Quit();
        #endif
    }
}

part 21 关卡菜单

关卡场景:插背景图,关卡1按钮图片,关卡2按钮图片,关卡2按钮图片,返回按钮图片
记住全部添加Collider方法(Box或Circle都行),各自写一个类响应OnMouseDown()
以LevelPageExit为例:

public class LevelPageExit : MonoBehaviour{
    public void OnMouseDown(){
        UnityEngine.SceneManagement.SceneManager.LoadScene(0);
    }
}

笔记整理

  1. Inspector面板各项属性
    Transform是场景每个对象的姿态
    Sprite Renderer是有贴图的对象
    Circle Collider 2D圆形碰撞检测
    Box Collider 2D矩形碰撞检测
    Rigidbody 2D是刚体引擎
    (Static就是不动的,例如弹簧的另一端树杈)
    (Dynamic有受力分析,如质量,滑动摩擦系数,滚动摩擦系数,重力加速度等,但不包括Collider碰撞)
    (Kinematic没有受力分析,只可以通过变换进行操作,例如用户手指拖动)
    Spring Joint 2D弹簧引擎
    (弹簧另一端要是Rigidbody属性)
    Script自己写的脚本
    Line Renderer线形渲染

  2. 获取他人与自己的坐标
    自己坐标:transform.position,其实是省略了this,或者说self
    他人坐标:public List birds;;
    private Vector3 originPos;
    originPos=birds[0].transform.position;

  3. 开关其他对象的引擎
    在Bird.cs中
    [HideInInspector]
    public SpringJoint2D sp;
    private void Awake(){sp=GetComponent();}
    在GameManager.cs中
    public List birds;
    birds[i].enabled=true;//第i只鸟的Bird.cs脚本生效
    birds[i].sp.enabled=true;//第i只鸟的SpringJoint2D属性生效
    birds[i].enabled=false;//第i只鸟的Bird.cs脚本失效
    birds[i].sp.enabled=false;//第i只鸟的SpringJoint2D属性失效

  4. 开关自身物理引擎(参考bird.cs)
    private Rigidbody2D rg;//默认Dynamic
    private void Awake(){rg=GetComponent();}
    rg.isKinematic=true;//不受动力学控制,可以让用户自由拖动
    rg.isKinematic=false;//受动力学控制,计算受力改变其速度

  5. 碰撞检测并取相对速度(参考pig.cs)
    private void OnCollisionEnter2D(Collision2D collision){print(collision.relativeVelocity.magnitude);}

  6. 改变自身贴图(参考pig.cs)
    public Sprite hurt;//hurt手动在面板传入
    private SpriteRenderer render;
    private void Awake(){render=GetComponent();}
    render.sprite=hurt;
    如果要通过外部来改变贴图,请参考第3点

  7. 动画制作(参考boom.cs)
    拖一帧到场景
    ctrl+6创建动画在Animation文件夹中
    给此对象写脚本
    给关键帧添加触发事件,例如自我销毁
    public void destroying(){Destroy(gameObject);}
    把对象拖进Prefabs文件夹中,场景对象设为失效

  8. 创建与销毁别的对象(参考pig.cs)
    public GameObject boom;//boom手动在面板传入
    Instantiate(boom,transform.position,Quaternion.identity);//原地生成爆作
    public GameObject score;//score手动在面板传入
    GameObject go=Instantiate(score,transform.position+new Vector3(0,0.8f,0),Quaternion.identity);//头顶生成分数
    Destroy(go,1.5f);//1.5秒后销毁

  9. 按钮跳转(参考GameManager.cs)
    在场景中创建UI-Image
    绑定Button事件
    绑定GameManager对象
    绑定对应事件public void Replay(){SceneManager.LoadScene(场景ID);}

  10. 音乐播放
    public AudioClip birdCollision;
    if(collision.gameObject.tag==“Player”)AudioSource.PlayClipAtPoint(birdCollision,transform.position);

  11. 面向对象
    封装:
    public可任意访问,可被子类对象继承,包括Inspector面板传入的参数
    protected可被自已访问,可被子类对象继承,不会在Inspector面板中显示
    private可被自已访问,不可被子类对象继承,不会在Inspector面板中显示
    继承:
    基础红鸟,派生出黄鸟与绿鸟(新增额外技能)
    多态:
    每个场景中的GameManager要用List维护所有的鸟,包括红鸟,黄鸟,绿鸟,可以声明列表元素是基类Bird

  12. 点击事件
    方法一:拉图片,加碰撞属性(必须!),自定义一个类,实现OnMouseDown()方法
    方法二:创建UI-Image,贴图赋值,加Button属性,拉入本场景Manager对象(绑定类中已实现各按钮方法),选择对应方法


记得在Edit-Preference-GI Cache处改缓存路径

猜你喜欢

转载自blog.csdn.net/cj1064789374/article/details/114626769