Unity是什么:
它就是一个2D/3D编辑器,同时又是一个游戏引擎
游戏引擎:使用者只需要调用这些功能,而不需要重写实现这些功能
资源导入的方法:
资源处理
2D游戏总的来说是依靠Sprites(图片精灵)
Sprite的类型
Default——默认纹理类型
Normal map——法线贴图资源
Editor GUI and Legacy ——用户界面的特图资源
Sprite——图片精灵
Cursor——鼠标光标
Cookie——光斑
Lightmap——光照贴图
Directional Lightmap——方向光贴图
Single Channel——单通道纹理
主角图片的创建
首先要将Single(单张图片) 变为Multiple(多张图片)
然后点击Sprite Editor 进行图片编辑
对图片进行分割
Automatic 自动
Grid By Cell Size 按照每一格像素切割
Gird By Cell Count 按照图片的数量切割
透明模式与颜色模式
场景的层级关系
不同的图片处于不同的层级
Sorting Layer 层级的设置
Order in Layer 层级的优先级
1、
同一层级的图片,优先级越大,图片越处在前面
项目开始:快捷Ctrl+P
主角组件创建
场景组件创建
常见的MonoBehaviour类的消息
快捷使用unity的各类方法(消息)
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中的动画系统
3、动画制作
打开动画片段后,选中游戏对象
进入动画编辑界面
开启记录关键帧,将图片拖入动画,将采样率设置为1,。
定位到第一秒中的位置
添加关键帧(keyframe)
最后停止记录关键帧
4、动画控制器设置
进入动画控制器中,建立各个动画之间的转换
添加两个触发器
在Idle转换Flap中,将触发器设置为转换的条件
Conditions:条件
然后通过代码进行控制
Animator anim;
anim = GetComponent<Animator>();
anim.SetTrigger("Flap");
完成后发现动画播放比较慢,将
取消。
它的作用就是是否等待上一个动画播放结束。
将Idle与Flap相互联系,可以让Idle和Flap相互转换,但注意在Flap转换Idle时,要将Has Exit Time 勾上,以防止在长时间没有Flap下,可以让Flap自动转换为Idle。
添加三个Text
分别是 Score、Gameover、HinText
用于显示分数、游戏结束、游戏提示
然后进行UI设计
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();//调用方法
}
方法一:通过动画实现
添加两个动画控制器,分别控制两个不同的背景进行滚动
注意动画播放的速度要改为匀速
方法二:通过代码控制
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;
}
}
}
public 让外界可以控制障碍回头的距离,方便与其他有相同的游戏物体区分。
if(width==0) 是让与障碍相同的脚本的游戏物体可以共用脚本。
注意:
要将刚体组件设置为Kinematic.,让游戏物体不受物理影响
在预制体中,给障碍添加一个得分的碰撞器,然后将碰撞器改为 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();
}
添加两个空节点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;
}
}
动态生成障碍,会造成内存碎片。
代码
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);
对象池的好处:在生成一个固定的游戏物体时,可以减少不断重复实例化带来的内存碎片。
实质:利用队列先进先出的特性,使障碍一直保持固定的数量,不增加内存的消耗。