本篇来说一下一个叫做愤怒的小鸟的游戏demo,紧接上一篇,主要谈怎么才能把代码写的更松散,降低耦合性,当然要从最基础的方法说起,设计模式在本篇会涉及但不会太多(主要是模板方法模式)(素材来自siki学院愤怒的小鸟初级案例教程,感兴趣的朋友可以自己去下载),话不多说,上图
好,我们可以看到图中一共有三种鸟,三种猪,三种建筑物,他们分别有不同的属性或方法,如果我直接将他们当做九种东西来
对待的话,会产生如下效果:
鸟类:每一个都有这几个方法:上弹弓,被拉出去,飞行,撞击,当然白色鸟有下蛋方法,蓝色鸟有分裂方法,那如果每一个鸟类
我都持有一个和弹弓有关的信息,弹弓实质上是图片+lineRenderer,也就是说我每一个鸟都需要持有一个x,y坐标组成的点,假设现在这个坐标为(100,200),而后来我觉得这个坐标不太合适,要把它改为(50,250),对每一个鸟我都要去修改这两个参数,这才
三个鸟我就要改6个参数,那如果有30,300,3000只鸟,我就不需要干其它的事情了.......所以说,这个参数是所有鸟都有的,而且
一定是等同的,我就可以将它放到他们公有的父类Bird类中,每一个鸟继承自Bird,这样不论弹弓的坐标怎么变,我只需要改这一个参数就行了,还有,比如,上弹弓,被拉出去,飞行等方法,也基本上是一样的,这些也可以放到这个Bird父类中,由它持有,比如我在飞行的过程中可能会下蛋或分裂,这样我在相应的子类中加入判断就好了,根据我的理解,这其实就是模板方法模式的精髓所在。下面列代码:
首先是Bird类,它是直接继承自Mono的,因为鸟类需要访问一些unity内置的组件和方法
using UnityEngine;
using UnityEngine.EventSystems;
public class Bird:MonoBehaviour
{
//是否已经按下鼠标左键
private bool isInputMouse;
public AudioClip onSlingClip;
public AudioClip fly;
public AudioClip selectBird;
public AudioClip birdCollision;
public bool OnSling = false;
public bool EverOnSling = false;
protected bool isAlive = true;
protected Rigidbody2D mRigidBody;
protected SpriteRenderer mSpriteRenderer;
AudioSource mAudioSource;
public virtual void OnCollisionEnter2D(Collision2D coll)
{
if (isAlive)
{
if ((coll.gameObject.tag == "Ground"||coll.gameObject.tag=="Pig") && EverOnSling)
{
BirdDie(3.0f);
MusicPlayer.instance.playMusic(mAudioSource, birdCollision);
}
else if (coll.gameObject.tag == "Block")
{
BirdDie(6.0f);
MusicPlayer.instance.playMusic(mAudioSource, birdCollision);
}
}
}
void Start()
{
mRigidBody = GetComponent
mSpriteRenderer = GetComponent
mAudioSource = gameObject.AddComponent
mAudioSource.playOnAwake = false;
isInputMouse = false;
}
//鸟上弹弓
public virtual void toSlingShot(GameObject objOnSling,GameObject leftSling,GameObject rightSling)
{
EverOnSling = true;
objOnSling.GetComponent
objOnSling.GetComponent
objOnSling.transform.position = (leftSling.transform.position + rightSling.transform.position) / 2;
OnSling = true;
CameraMove.cameraMove.setDeltaVec(objOnSling.transform.position);
}
// 在弹弓上被拉 基准点,拉动的范围 range range/3 拉动的速度,LineRenderer组件
public void pullOnSling(GameObject leftSling,float range,float speed,LineRenderer left,LineRenderer right)
{
if (!OnSling)
return;
if (EventSystem.current.IsPointerOverGameObject()|| EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
return;
if(!isInputMouse&&OnSling&&(Input.GetMouseButtonDown(0)||(Input.touchCount==1&&Input.GetTouch(0).phase==TouchPhase.Began)))
{
MusicPlayer.instance.playMusic(MusicPlayer.instance.GetComponent
isInputMouse = true;
}
float x = leftSling.transform.position.x;
float y = leftSling.transform.position.y;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
//让鸟跟随到鼠标移动的位置
if(OnSling&&Physics.Raycast(ray,out hit)&&Input.GetMouseButton(0))
{
transform.position = Vector3.Lerp(transform.position, hit.point, Time.deltaTime*speed);
}
//判定鸟的坐标防止越界
if(Vector2.Distance(transform.position,leftSling.transform.position)>=range)
{
transform.position = (transform.position - leftSling.transform.position).normalized * range+ leftSling.transform.position;
}
//设置LineRenderer组件将位置设置正确
left.SetPosition(0, left.transform.position);
left.SetPosition(1, transform.position);
right.SetPosition(0, right.transform.position);
right.SetPosition(1, transform.position);
//设置速度,弹出鸟
if (Input.GetMouseButtonUp(0))
{
Vector3 vecTail = (left.transform.position+right.transform.position)/2;
OnSling = false;
mRigidBody.gravityScale = 0.5f;
left.SetPosition(1, left.transform.position);
right.SetPosition(1, right.transform.position);
Fly(vecTail - transform.position);
}
}
//鸟被弹弓弹出
public virtual void Fly(Vector3 velocity)
{
MusicPlayer.instance.playMusic(mAudioSource,fly);
mRigidBody.velocity = velocity*7;
}
//鸟死亡
protected virtual void BirdDie(float tim)
{
isAlive = false;
GameManager.instance.minusBirdCount(tim - 0.2f);
Destroy(gameObject,tim);
}
}
而其它三种鸟则是继承自这个Bird父类的,
首先是Bird1:红色的鸟
using UnityEngine;
public class Bird1 : Bird
{
public Sprite beingHit;
public override void OnCollisionEnter2D(Collision2D coll)
{
base.OnCollisionEnter2D(coll);
if((coll.gameObject.tag=="Ground"||coll.gameObject.tag=="Block"||coll.gameObject.tag=="Pig")&&EverOnSling)
{
mSpriteRenderer.sprite = beingHit;
}
}
}
接下来是Bird2:白色的鸟
using UnityEngine;
public class Bird2 : Bird
{
//是否能下蛋
private bool canLay = false;
public RuntimeAnimatorController birdController;
public AudioClip eggClip;
public GameObject Egg;
public override void OnCollisionEnter2D(Collision2D coll)
{
base.OnCollisionEnter2D(coll);
if(coll.gameObject.tag=="Block"||coll.gameObject.tag=="Pig")
{
canLay = false;
}
}
//下蛋
public void CreateEgg()
{
MusicPlayer.instance.playMusic(GetComponent
Instantiate(Egg, transform.position + new Vector3(0, -0.3f, 0), Quaternion.identity);
}
public override void Fly(Vector3 velocity)
{
base.Fly(velocity);
canLay = true;
}
void FixedUpdate()
{
if(canLay&&(Input.GetMouseButtonDown(0)||(Input.touchCount == 1&&Input.GetTouch(0).phase == TouchPhase.Began)))
{
CreateEgg();
canLay = false;
FlyAway();
}
}
private void FlyAway()
{
mRigidBody.velocity = new Vector2(3, 3);
mRigidBody.gravityScale = 0;
gameObject.GetComponent
BirdDie(3.0f);
}
public override void toSlingShot(GameObject objOnSling, GameObject leftSling, GameObject rightSling)
{
base.toSlingShot(objOnSling, leftSling, rightSling);
GetComponent
}
}
接下来是Bird3:蓝色的鸟
using UnityEngine;
public class Bird3 : Bird
{
public Sprite beingHit;
private bool canClone = true;
public GameObject bird3Clone;
public override void OnCollisionEnter2D(Collision2D coll)
{
base.OnCollisionEnter2D(coll);
if ((coll.gameObject.tag == "Ground" || coll.gameObject.tag == "Block" || coll.gameObject.tag == "Pig") && EverOnSling)
{
mSpriteRenderer.sprite = beingHit;
}
}
public void CloneMore()
{
GameObject more = Instantiate(bird3Clone,transform.position+new Vector3(0,0.1f,0),Quaternion.identity);
more.GetComponent
more=Instantiate(bird3Clone, transform.position + new Vector3(0, -0.1f, 0), Quaternion.identity);
more.GetComponent
more =Instantiate(bird3Clone, transform.position + new Vector3(0, 0.2f, 0), Quaternion.identity);
more.GetComponent
more = Instantiate(bird3Clone, transform.position + new Vector3(0, -0.2f, 0), Quaternion.identity);
more.GetComponent
}
void FixedUpdate()
{
if(canClone&&EverOnSling&&!OnSling&&(Input.GetMouseButtonDown(0)||(Input.touchCount == 1&&Input.GetTouch(0).phase == TouchPhase.Began)))
{
canClone = false;
CloneMore();
}
}
}
当然,我的代码写的明显问题很多,有很多东西应该封装成方法更合适的,在这里为了体现模板方法模式的重要性,我们暂且撇开其它的不管,可以看到,用了继承以后,子类的规模明显小了特别特别特别多,更重要的是与其它模块相关联(也就是产生耦合的地方绝大多数都放在了父类中),如果其它模块有变更,子类是基本不需要改动的,只需要变更父类中相关代码,当子类增多的时候,这种设计模式的优势就体现出来了。
差点忘了,分裂出来的蓝色鸟也是一种鸟,我称它为BirdClone,利用Bird中现成的模板方法,这个类是不是变得特别简单呢,没错!
using UnityEngine;
public class Bird3Clone : Bird
{
protected override void BirdDie(float tim)
{
isAlive = false;
Destroy(gameObject, tim);
}
}
就拿鸟类开刀好了,猪和砖块也是类似的道理,没什么好说的,只是有一点需要注意:那就是游戏管理类和音乐播放类的处理,
其实最好不好直接用单例模式,通过一个facade中介类,将鸟类,弹弓,猪 这三种游戏物体 与 管理者(游戏管理和音乐播放) 耦合的部分都挤到facade中,facade做成单例,这样通过调用facade中的方法来实现响应的功能会更好,有变更也只需要更改facade中的内容,不过案例较小的情况下差别是不大的,在这里我们也不特别去讨论,以后会有更多的篇章来介绍解耦和和代码复用等使整个系统架构更清晰的方法。