享元模式!
几乎任何时候都需要思考的,一个实例是可以复用的嘛!还是每一个调用者都需要有一份新的?
在游戏属性管理时,我们可以看到属性可以被切割两次:
- 共享or不共享
- 变化or不会变化
这部分是大家都有的属性。而且固定的。我出生时就是这样的数据。基本不会变化。它成为了一种配置。
比如超级士兵最高
public class BaseAttr
{
private int m_MaxHp;
private string m_AttrName;
private float m_speed;
public BaseAttr(int hp,float speed,string name)
{
this.MaxHp=hp;
this.MoveSpeed=speed;
this.AttrName=AttrName;
}
public int GetMaxHp(){return m_MaxHP}
public float GetMoveSpeed(){return m_speed}
public string GetAttrName(){return m_AttrName}
}
我们可以在以上这部分内容之后继承一个新的类,增加我们的新属性。
比如我们新建一个EnemyBaseAttr类,表示怪物的属性,怪物在游戏中的属性会比普通玩家一个【暴击率】
接下来我们建立一些类来处理属性中【会变化】的部分
ICharacterAttr,SoldierAttr,EnemyAttr,这些定义角色属性中会按游戏执行而变化的部分,属于各角色对象自己管理的部分。
它会有一个BaseAttr。
public abstact class ICharacterAttr
{
protected BaseAttr m_BaseAttr=null;
protected int m_NowHp=0;
protected IAttrStrategy m_AttrStrategy=null;
//这个是我们在……嗯,策略模式里建立的策略类的属性类,方便进行属性计算的。
public ICharacterAttr(){}
protected void SetBaseAttr(BaseAttr BaseAttr){
m_BaseAttr=BaseAttr;//我猜是因为baseattr只有一个,所以这边传过来让它知道就可以了。
}
public void SetAttrStrategy(IAttrStrategy theAttrStrategy)
{
m_AttrStrategy=theAttrStrategy;
}
//获取属性的计算策略
public IAttrStrategy GetAttrStrategy()
{
return m_AttrStrategy;
}
/*
获取hp、最大hp(引用baseattr)(虚函数),获取移动速度(引用baseattr)(虚函数),获取属性名称(引用baseattr)(虚函数)
*/
除了以上这些内容以外。
还有哪些函数:
1. 回满当前hp值。m_nowHp=baseattr.GetMaxHp()
2. 初始化角色属性。virtual函数。initAttr(){m_AttrStrategy.InitAttr(this);FullNowHp();}
3. 获得攻击加成。(这个与策略类有关)m_AttrStrategy.GetAtkPlusValue(this);
4. 获得被武器攻击后的伤害值。
public void CalDmgValue(ICharacter Attacker)
{
int AtkValue=Attacker.GetAtkValue();
AtkValue -=m_AttrStrategy.GetDmgDescValue(this);
m_NowHP -=AtkValue;
}//这里直接涉及到很多计算的情况了。
}
在以上这个类里面的一些虚函数,可以给子类重写。比如获得属性名称,比如初始化角色属性。
子类就是SoiderAttr和EnemyAttr
一些特殊化的内容就可以在这里实现。比如我们的士兵。会随着等级增加生命值。那么在获取最大生命值时,在这里就被特殊的调用为一个baseattr+一个特殊的、会变的属性a_addMaxhp.
而敌人则是有一个暴击率和获得暴击率、修改暴击率的方法。
public class SoldierAttr : ICharcterAttr
{
protexted int m_SoldierLV;//这两个属性就是属于会变的属性
protexted it m_AddMaxHp;
public SoldierAttr(){}
public void SetSoldierAttr(BaseAttr BaseAttr)
{
base.SetBaseAttr(BaseAttr);
m_SoiderLV=1;
m_AddMaxHp=0;
}
//还有一些函数,比如设置等级。获取等级。获取最大hp(base.GetMaxhp+m_addMaxHp)。设置新增的最大生命力。
}
然后是用属性工厂来分别管理游戏中的3个属性对象。
这个工厂类是可以提供给【调用者】以一个对应的attr实例。这个attr里面包含了会【变化】和不会【变化】的东西。
我们建立了一个叫做AttrFactory的类。它基本上只会有一个。它手上有士兵、怪物、武器的三个工厂,这个意思是,它手上有很多工厂的内容。
我感觉这个是这样用的。就是我们有一个表格,会这样配置我们的怪物
编号 名称 血量 速度
1 新兵 200 2.0
20 勇士 201 2.0
然后这些数据在游戏开始前被导入到这个工厂类中。被储存在士兵Attrdict中。
在创建这个attrFactory时就读入这个表格。这让这个类的其他方法能够在产生属性对象的过程中,获取对应的共享属性对象。
好。接下来就是重头戏。以下是这个类其中的一个方法。
public class AttrFactory : IAttrFactory
{
public overide SoidierAttr GetSoidierAttr(int AttrID)
{
//这里的id就是之前放到士兵dict中的key。如果找到,则创建一个soldierAttr返回。如果找不到,则报错。
如果找到:
SoldierAttr NewAttr =new SoldierAttr();
NewAttr.SetSoiderAttr(m_SoldierAttrDB(AttrID));//看!不可变化的内容被包到了可变化的内容里。
return NewAttr;
}
}
如果没有flyweight,那要怎么样实现工厂呢?
嗯。就是在工厂类里getsolderAttr(id)
switch(id)
4:return new SoldierAttr(“新兵”,200,1.0);
10:return new SoldierAttr(“战士”,201,1.1);
弊端在于,一个麻烦,不同的工厂都要重写,一个是没有实现共同的属性(配置)和特别的属性(临时)。
扩展
吃了一顿之后,忘了要扩展什么了。
噢,对,想想这个享元模式可以用在什么其他地方。
它包了一个包含了公用的对象的特殊对象的制作过程。除了形式上有意义以外,性能上也很重要。以后遇到有公用的对象的情况就可以用它。外部不需要知道制作过程。哇哦。结论中说可以用在对象池上。
就是有一些对象,是baseattr,在创建子弹时,其实是调用其中一个baseattr.