撰写了文章 发布于 2017-11-12 16:43:46
[Unity2D]全民打砖块开发实例(上)
好久没发新文章了,正好最近做了一个典型的打砖块游戏,很适合在我的基础上抛砖引玉做一些新的修改和变化,对应的素材已上传网盘,欢迎和我一起构建你们自己的“全民打砖块”来,闲话少叙,开始吧
添加素材及构建菜单
主流程由开始菜单→关卡1→关卡2→。。。→胜利画面,过程中任意次失败(平台未接到弹球)则跳转到失败画面→开始菜单,依次循环。
平台Paddle及可消除砖块Bricks大小一致为矩形,尺寸128x41,小球Ball添加刚体及碰撞器
背景固定800x600,可自定义素材对应分辨率进行修改
使用UI系统构建菜单跳转,以StartMeun中Start Button为例
构建空物体挂载LevelManager脚本负责之后的场景切换功能,并对应在OnClick()中调用
public void LoadLevel(string name){
SceneManager.LoadScene(name);
}
public void QuitRequest(){
Application.Quit();
}
胜利与失败界面同理,均添加Restart按钮作为对应的逻辑跳转
单例模式
在StartScene中添加空物体挂载MusicPlayer脚本负责背景乐生成
static MusicPlayer instance=null;
void Awake(){
if(instance!=null){
Destroy(gameObject);//防止在重新进入StartMenu时出现多个MusicPlayer
}else{
instance=this;
DontDestroyOnLoad(gameObject);//Makes the object target not be destroyed automatically when loading a new scene.
}
}
意图:单件模式保证应用只有一个全局惟一的实例,并且提供一个访问它的全局访问点。
单件模式讲得就是如何实例化“唯一的对象”!但这有什么用呢?有些时候我们只需要一个,比如说:缓存、对话框、注册表的对象、日志对象等。的确,就算不用单件模式,我们也可以通过静态类变量、静态方法和适当的访问修饰符来实现相同的功能,但了解单件的运作方式仍然很有用。
结构:包括防止其他对象创建实例的私有构造函数、保存惟一实例的私有变量和全局访问接口等。
效果:单件提供了全局惟一的访问入口,因此易于控制可能发生的冲突。单件是对类静态函数的一种改进,首先它避免了全局变量对系统的污染。如果将对象赋给一个全局变量,那么必须在程序一开始就创建好对象,若这个对象非常消耗资源,而程序在这次执行过程中又一直没有用到它,就形成了浪费;其次正常类可以有子类,可以定义虚函数,具有多态性。而类中的静态方法是不能定义为虚函数的,因此不具有多态性。单件模式可以扩展为多件,即允许有受控的多个实例存在。
适用场合:当类只能有一个实例存在,并且可以在全局访问时。这个惟一的实例应该可以通过子类实现扩展,并且用户无须更改代码即可使用。我们前面介绍的工厂类经常被实例化为全局惟一的单件,可能的单件还有管理日志的对象、关键字生成对象和外部设备接口对象等。
注意场景和构建物体的纵深关系,比较合适的前后关系为Background Z=5,Sprite Z=0,Camera Z=-10
添加碰撞体并选择合适的ColliderType
在我们为场景及内部物件添加合适的碰撞及刚体时,可以先看一下如下的表格
表格内容描述了选择不同ColliderType时对应使用的是OnCollisionEnter还是OnTriggerEnter的方法。
对比可知,在下图的场景碰撞时,只有最底部的检测碰撞需要在BoxCollider中勾选isTrigger,并使用OnTriggerEnter方法来进行LoadLevel的跳转,其余三块只需挂载弹性PhysicsMaterial2D来实现,具体代码如下
private LevelManager levelManger;
void Start(){
levelManager=GameObject.FindObjectOfType<LevelManager>();//实现在每个场景中自动调用LevelManger中方法,不需要手动拖拽该物件实现调用。
}
void OnTriggerEnter2D(Collider2D col){
levelManger.LoadLevel("LoseMenu");
}
实现平台移动及小球跟随
为Paddle添加对应的PolygonCollider,对应脚本实现在平台X轴上跟随鼠标轨迹功能
void MoveWithMouse(){
Vector3 paddlePos=new Vector3(0.5f,this.transform.position.y,0f);
float mousePosInBlock=Input.mousePosition.x/Screen.width*16;
paddlePos.x=Mathf.Clamp(mousePosInBlock,1.05f,15f);
this.transform.position=paddlePos;
}
初始状态下,小球应该粘附在平台上,直到玩家点击鼠标左键右小球弹射后停止跟踪,对应Ball脚本如下
private Paddle paddle;
bool hasStarted=false;
void Start(){
paddle=GameObject.FindObjectOfType<Paddle>();
Vector3 paddleToBallVector=this.transform.position-paddle.transform.position;
}
void Update(){
if(!hasStarted){
this.transform.position=paddle.transform.position+paddleToBallVector;
if(Input.GetMouseButtonDown(0)){
hasStarted=true;
this.GetComponent<RigidBody2D>().velocity=new Vector2(3.0f,8.0f);
}
}
}
创建砖块及多次碰撞消失效果
创建多种类型砖块,并为其赋予SpriteSheet及对应的最大撞击次数后消失的属性
SpriteSheet的创建方式在此不做赘述,我个人游戏版本中仅包含2次及3次撞击效果,效果很简陋,大家完全可以自由创作再加工
为每种Brick添加相同脚本,并实现根据Array.Length+1来判断游戏,具体实现如下
private LevelManager levelManager
private int timesHits;//被碰撞次数
private bool isBreakable
public Sprite[] hitSprites;//手动添加对应SpriteSheet
public static int breakableCount=0;//使用静态变量方便调用,但由于无法被序列化,所以在Inspector中观测不到
void Start(){
timeshits=0;
levelManager=GameObject.FindObjectOfType<LevelManager>();
isBreakable=(gameObject.tag=="Breakable");//注意为可破坏砖块添加对应Tag
if(isBreakable){
breakableCount++;//统计Scene中所有的砖块数量
}
}
void OnCollisionEnter2D(Collision2D col){
if(isBreakable){
HandleHits();
}
}
void HandleHits(){
timesHits++;
int maxHits=hitSprites.Length+1;
if(timesHits>=maxHits){
Destroy(gameObject);
breakableCount--;
levelManager.BrickDestroyed();//该方法见LevelManager脚本更新
}else{LoadSprites();}
}
void LoadSprites(){
int spriteIndex=timesHits-1;
if(hitSprites[spriteIndex]//防止SpriteSheet缺失){
this.GetComponent<SpriteRenderer>().sprite=hitSprites[spriteIndex];
}
}
LevelManager脚本更新BrickDestroyed方法如下
public void BrickDestroyed(){
if(Bricks.breakableCount<=0){
LoadNextLevel();
}
}
public void LoadNextLevel(){
Bricks.breakableCount=0;
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex+1);
}
基本上如果按照这些步骤的话,基础的游戏框架和玩法已经实现了差不多了,剩余的一些部分今日晚些更新~欢迎继续跟进!
目录