虚拟实验室引擎的开发和实现(五、VPlace 和 VItem)

在前面的章节里,我定义了一个抽象类VObject,作为虚拟实验室项目的“祖先”,接下来,我将实现两个具体类:VPlace和VItem,分别表示“场景类”和“物品类”。

image


VPlace

场景

所谓场景,实际上相当于背景,像地板这些永远不会覆盖在人物之上的东西统统可以归咎于场景。引擎中的场景应该具有以下几个特点:

  1. 不会轻易改变位置;
  2. 永远位于物品下方,不存在空间排序问题及遮挡问题;
  3. 在同一时刻,场景只能有一个;
  4. 场景一般是静止不动的,因此可以用一副图片来表示场景;
  5. 场景里有很多物品,这些物品按照一定的空间顺序排列。

根据场景的分类,还可以进一步抽象成VStreet(街道)、VRoom(房间)等具体信息,因此,在设计VPlace时,尽量把相关的属性放在一起。

实现VPlace类

VPlace类继承自VObject:

public class VPlace : VObject
{
    //…………
}

在里面添加一个属性,用来记录该场景中有哪些物品(包括人物,在这里我们把物品和人物同一对待,在下一级类,VRoom,才把这些区分开来):

public ObservableCollection<VItem> Items { get; set; }

按照父类中构造函数Build的顺序,重载Initialize方法和CreateUI方法(因为VPlace和VOjbect相比,没有多少“特殊”的属性需要添加,因此可以不需要重载VObject的Parse方法):

image

protected override void CreateUI()
{
    UI = new UIPlace(this);
}

protected override void Initialize()
{
    Items = new ObservableCollection<VItem>();
}

最后,添加一个AddItem和一个RemoveItem的方法,用来添加及删除该场景的物品:

public virtual void AddItem(VItem item)
{
    Items.Add(item);
}

public virtual void RemoveItem(VItem item)
{
    Items.Remove(item);
}

由于Items属性是ObservableCollection<T>类型的,因此在改变场景中物品时,不需要写额外的事件通知机制,这点是我喜欢的。

接下来,就可以这样构造VPlace了:

String placeString = "<place name='NBA' width='1800' height='1200'
    centerX='0' centerY='0' x='-800' y='-500' file='engine1/place1/place.xml' />";
VPlace vp = new VPlace(placeString);

VItem

物品

物品,大概相当于前景。例如桌子、柜子等等。这些东西在渲染的时候,可能需要按照它们所处的空间位置排列,造就遮挡的效果。根据唯物主义理论,人物也是一种物品,不过和桌子比,人物会动,需要特别处理,我会专门用一个VPerson类来表示,和场景不同,虚拟使显示引起中涉及的物品可能会有以下特征:

  1. 有可能改变位置,永远位于背景上方;
  2. 同一个场景中,有多个物品,这些物品根据特定的规则,相互遮挡;
  3. 一个物品在一个时刻只能位于一个场景中;
  4. 在不同的条件下(例如时刻,鼠标悬浮等),物品有可能呈现不同的效果于用户交互。

在虚拟实验室引擎的设计过程中,我用VItem来表示“物品类”。

实现VItem

和VPlace一样,VItem也是继承自VObject类的,按照以上特点,我在VObject的属性中,添加了几个新属性。

public class VItem : VObject
{
    //…………
}

增加一个属性,表示该物品位于哪个场景:

protected VPlace m_Place;

public VPlace Place
{
    get
    {
        return m_Place;
    }
    set
    {
        if (m_Place != value)
        {
            m_Place = value;
            OnPropertyChanged("Place");
        }
    }
}

根据上面总结出来的特点的第四条:在不同的条件下(例如时刻,鼠标悬浮等),物品有可能呈现不同的效果于用户交互,我把这些不同的效果称作“动作(VAction)”,用两个集合类来表示动作集合。:

public Dictionary<String, VAction> Actions { get; set; }
public ObservableCollection<String> ActionNames { get; set; }

此外,还有一个ActionID的属性,表示当前正在做的动作:

protected String m_ActionID;

public String ActionID
{
    get
    {
        return m_ActionID;
    }
    set
    {
        if (m_ActionID != value)
        {
            m_ActionID = value;
            OnPropertyChanged("ActionID");
        }
    }
}

最后,根据VObject的Build顺序,重写以下函数:

protected override void Parse(XElement xElement)
{
    base.Parse(xElement);
    ActionID = xElement.Attribute("actionid", String.Empty);
}

protected override void CreateUI()
{
    UI = new UIItem(this);
}

protected override void Initialize()
{
    Actions = new Dictionary<string, VAction>();
    ActionNames = new ObservableCollection<string>();
}

关于VAction

VAction是VItem中的动作,定义如下:

public class VAction
{
    public String Name { get; set; }            //动作名
    public VRectangle[] Rectangle { get; set; } //动画组成
    public String Next { get; set; }            //播放完后的动画
    public int FPS { get; set; }                //帧率

    public VAction(String name, String next, int fps,
        params VRectangle[] rectangle)
    {
        Name = name;
        Next = next;
        Rectangle = rectangle;
        FPS = fps;
    }
}

public struct VRectangle
{
    public int X;
    public int Y;
    public int Width;
    public int Height;
}

用一个Xml文件来描述这些VAction:

<?xml version="1.0" encoding="utf-8"?>
<item image="girl.xml.png" width="147" height="144">
  <action name="facerightdown" fps="10" next="facerightdown">
    <rectangle x="1176" y="0" />
    <rectangle x="1323" y="0" />
    <rectangle x="1470" y="0" />
    <rectangle x="1617" y="0" />
    <rectangle x="1764" y="0" />
    <rectangle x="1911" y="0" />
    <rectangle x="2058" y="0" />
    <rectangle x="2205" y="0" />
  </actions>
  <action name="faceright" fps="10" next="faceright">
    <rectangle x="0" y="0" />
    <rectangle x="147" y="0" />
    <rectangle x="294" y="0" />
    <rectangle x="441" y="0" />
    <rectangle x="588" y="0" />
    <rectangle x="735" y="0" />
    <rectangle x="882" y="0" />
    <rectangle x="1029" y="0" />
  </actions>
</item>

image

关于Next属性

这里特别说明一下Next属性,所谓Next,就是当该Action执行完后,下一个需要执行的Action,如果想不停地执行某个Action,很简单,把Next设置成自身即可。

 

开工了,大家作业完成的如何?周老师已经准备好了皮鞭:

untitled

看谁敢不听话!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值