Tôi là một sinh viên đại học, và phát triển trò chơi là một sở thích nhỏ đối với tôi. Tôi đang viết một trò chơi mới và tôi đang tìm kiếm một cách tiếp cận mới để phát triển công cụ trò chơi để làm cho nó linh hoạt hơn (và cho phép cung cấp cho mình một nền tảng vững chắc cho những nỗ lực trong tương lai). Tôi đã tìm thấy một vài bài viết về cách thay vì dựa vào tính kế thừa sâu sắc để thể hiện các thực thể trò chơi (Điều này có thể dẫn đến các nút gốc và lá nặng trong cây thừa kế của bạn, cũng như kế thừa kim cương trong một số trường hợp), một cách tiếp cận tốt hơn là mô hình hóa logic của bạn trong các thành phần (Tốt hơn nữa, hành vi và thuộc tính).
Dưới đây là một bài trình bày minh họa điều này: http://www.gdcvault.com/free/gdc-canada-09 "Lý thuyết và thực hành của Architec Thành phần đối tượng trò chơi ..."
Tôi đang thực hiện điều này bằng XNA và C #. Tôi có một lớp Thực thể:
class Entity
{
private Game1 mGame;
private Dictionary<AttributeType, object> mAttributes;
private Dictionary<BehaviorType, Behavior> mBehaviors;
public Entity(Game game)
{
mGame = game;
mAttributes = new Dictionary<AttributeType, object>();
mBehaviors = new Dictionary<BehaviorType, Behavior>();
}
public Game GetGame()
{
return mGame;
}
//Attributes
public void SetAttribute<T>(AttributeType attributeType, T value)
{
if (mAttributes.ContainsKey(attributeType))
{
mAttributes[attributeType] = value;
OnMessage<T>(MessageType.ATTRIBUTE_UPDATED, attributeType, value);
}
else
{
mAttributes.Add(attributeType, value);
OnMessage<T>(MessageType.ATTRIBUTE_CREATED, attributeType, value);
}
}
public T GetAttribute<T>(AttributeType attributeType)
{
if (!mAttributes.ContainsKey(attributeType))
{
throw new KeyNotFoundException("GetAttribute: Attribute with type: " + attributeType.ToString() + " not found.");
}
return (T)mAttributes[attributeType];
}
public bool HasAttribute(AttributeType attributeType)
{
return mAttributes.ContainsKey(attributeType);
}
//Behaviors
public void SetBehavior(BehaviorType behaviorType, Behavior behavior)
{
if (mBehaviors.ContainsKey(behaviorType))
{
mBehaviors[behaviorType] = behavior;
}
else
{
mBehaviors.Add(behaviorType, behavior);
}
}
public Behavior GetBehavior(BehaviorType behaviorType)
{
if (!mBehaviors.ContainsKey(behaviorType))
{
throw new KeyNotFoundException("GetBehavior: Behavior with type: " + behaviorType.ToString() + " not found.");
}
return mBehaviors[behaviorType];
}
public bool HasBehavior(BehaviorType behaviorType)
{
return mBehaviors.ContainsKey(behaviorType);
}
public Behavior RemoveBehavior(BehaviorType behaviorType)
{
if (!mBehaviors.ContainsKey(behaviorType))
{
throw new KeyNotFoundException("RemoveBehavior: Behavior with type: " + behaviorType.ToString() + " not found.");
}
Behavior behavior = mBehaviors[behaviorType];
mBehaviors.Remove(behaviorType);
return behavior;
}
public void OnUpdate(GameTime gameTime)
{
foreach (Behavior behavior in mBehaviors.Values)
{
behavior.OnUpdate(gameTime);
}
}
public void OnMessage<T>(MessageType messageType, AttributeType attributeType, T data)
{
foreach (Behavior behavior in mBehaviors.Values)
{
behavior.OnMessage<T>(messageType, attributeType, data);
}
}
}
Trong đó "AttributionType" và "BehaviorType" chỉ là enum:
public enum AttributeType
{
POSITION_2D,
VELOCITY_2D,
TEXTURE_2D,
RGB_8888
}
Lớp hành vi là một siêu lớp của tất cả các hành vi khác; các hành vi có nghĩa là khép kín và mô-đun (Ví dụ: tôi có thể có SpriteBatchRenderBehavior chỉ biết cách hiển thị một thực thể cho SpriteBatch, có thể sử dụng được trong bất kỳ trò chơi mới nào sử dụng khung này).
abstract class Behavior
{
//Reference to owner to access attributes
private Entity mOwner;
public Behavior(Entity owner)
{
mOwner = owner;
}
public Entity GetOwner()
{
return mOwner;
}
protected void SetAttribute<T>(AttributeType attributeType, T value)
{
mOwner.SetAttribute<T>(attributeType, value);
}
protected T GetAttribute<T>(AttributeType attributeType)
{
return (T)mOwner.GetAttribute<T>(attributeType);
}
public abstract void OnUpdate(GameTime gameTime);
public abstract void OnMessage<T>(MessageType messageType, AttributeType attributeType, T data);
}
Vì vậy, một thực thể có một túi các thuộc tính được gắn nhãn (POSITION_2D, TEXTURE_2D, v.v.), mỗi hành vi có thể truy cập các thuộc tính của chủ sở hữu của chúng thông qua các phương thức get / set. Tôi đang gặp vấn đề trong đó các thuộc tính từ thực thể của tôi cần được chia sẻ giữa các hành vi (không chắc đây có thực sự là vấn đề không). Về cơ bản, nếu tôi có một hành vi thay đổi vị trí của một thực thể trong thế giới trò chơi, nó sẽ sửa đổi thuộc tính vị trí của thực thể. Sau đó tôi thêm SpriteBatchDrawBehavior vào thực thể, tùy thuộc vào thực thể có vị trí (để nó biết nơi vẽ thực thể). Vấn đề của tôi là, nếu tôi thêm hành vi vẽ trước hành vi di chuyển, không có gì đảm bảo rằng thực thể sẽ có được một thuộc tính vị trí (trong trường hợp đó, một khóa không tìm thấy ngoại lệ sẽ bị ném).
Tôi luôn có thể đảm bảo thực thể có một vị trí bằng cách thêm một hành vi đảm bảo nó S have có một vị trí trước khi thêm một hành vi vẽ (hoặc bất kỳ hành vi nào khác phụ thuộc vào thuộc tính này), tuy nhiên điều này sẽ làm cho các hành vi này được ghép nối theo cách chúng không nên
Tùy chọn khác là kế thừa hành vi vẽ từ hành vi di chuyển kế thừa từ hành vi vị trí (nhưng điều này đưa chúng ta trở lại vấn đề với sự thừa kế kim cương, v.v. và các lớp con không liên quan đến THỰC TẾ với các lớp cha mẹ của chúng, khác hơn là có sự phụ thuộc tương tự).
Thêm một lựa chọn nữa là viết một kiểm tra trong các hành vi của tôi như: "Nếu chủ sở hữu của tôi không có vị trí, thì hãy tạo thuộc tính vị trí cho anh ta", nhưng sau đó, điều này đặt ra câu hỏi: nên chọn giá trị mặc định nào cho thuộc tính mới ? Vân vân.
Bất kỳ đề xuất? Tôi đang nhìn cái gì đó?
Cảm ơn!