虚拟 Monopoly





5.00/5 (3投票s)
对最受欢迎的家庭游戏之一《大富翁》的改造。
引言
虚拟大富翁使用 AIO 平台,最多可供 6 名玩家玩。将 AIO 像原始棋盘游戏一样平放在桌子上,玩家将能够通过触摸传感器与游戏互动。其理念是尽可能保留棋盘游戏中亲身体验的部分,允许玩家拖动自己的棋子移动允许的格数,滑动手指翻牌,拖动新购买的房产到指定位置,并与虚拟银行系统互动进行交易。27 英寸 AIO 平台的屏幕空间将提供舒适愉悦的视角,同时为每个玩家提供游戏板周围(屏幕边缘)的指定区域,称为“用户角”,以在游戏过程中访问统计信息。玩家还可以选择通过智能手机管理他们的用户角。高清图形将显示令人惊叹的视觉效果。这将是迄今为止最令人愉悦和互动的大富翁类棋盘游戏。
背景
虚拟大富翁是经典游戏《大富翁》的彻底重制。游戏的图形主题将采用太空时代的形式。房产将是空间站和基地,游戏棋子将是与太空相关的物体,如航天飞机,街道将被行星、星座和星系取代。几乎所有人都熟悉大富翁,如果不熟悉,维基百科提供了所有历史信息以及原始游戏的玩法。游戏玩法说明将以 PDF 格式随虚拟大富翁一起提供。
这款游戏将把大富翁的乐趣带入新一代,同时具有一定的教育意义。在玩游戏时,玩家将通过可识别、逼真且吸引人的图形了解宇宙。预计将看到的星系有银河系、仙女座、半人马座和欧米茄星系等。铁路将由四颗恒星取代,分别是天狼星、波江座ε、大角星和太阳。监狱将是一个超大质量黑洞。
使用代码
该游戏将使用 Visual C++ 创建。通过使用 .NET 语言,它将为与朋友在线游戏铺平更轻松的道路。标准 Windows 8 API 包含了所有我需要实现此功能的触摸功能。
游戏的视觉/图形将在我使用更简单的位图图形(作为占位符)使代码稳定运行后创建。
一个重要的功能将是掷骰子功能。可以选择使用 AIO 提供的电子骰子,或者使用虚拟骰子,后者将在指定区域滑动两根手指来掷出。我将为每个骰子使用两个 1-6 之间的随机数生成器,并返回每个值,以便显示正确的骰子面并允许玩家移动相应的格数。
int rolls = 0;
while(rolls <= 1) {
faceValue = 1 + rand() % 6;
if(rolls == 0) {
dice.setValue1(faceValue);
}
else if(rolls == 1) {
dice.setValue2(faceValue);
}
else {
return;
}
rolls++;
}
如您所见,创建了类来保存信息。将有骰子、棋盘和玩家的类,以及其他游戏组件,如随机卡片。`Player` 类将保存位置、金钱、房产、卡片等值。在游戏中使用图形时,这些类将非常重要,因为需要检索信息以显示其正确的图形。
ref class Bank {
private:
double _500s;
double _100s;
double _50s;
double _20s;
double _10s;
double _5s;
double _1s;
public:
Bank() {
Clear();
}
void set(double a, double b, double c, double d, double e, double f, double g) {
_500s = a;
_100s = b;
_50s = c;
_20s = d;
_10s = e;
_5s = f;
_1s = g;
}
double get500s() {
return _500s;
}
double get100s() {
return _100s;
}
double get50s() {
return _50s;
}
double get20s() {
return _20s;
}
double get10s() {
return _10s;
}
double get5s() {
return _5s;
}
double get1s() {
return _1s;
}
double getTotal() {
return (_500s * 500) +
(_100s * 100) +
(_50s * 50) +
(_20s * 20) +
(_10s * 10) +
(_5s * 5) + _1s;
}
void Clear() {
_500s = -1;
_100s = -1;
_50s = -1;
_20s = -1;
_10s = -1;
_5s = -1;
_1s = -1;
}
};
游戏板动画示例如下:
void pictureBox1_Paint(Object ^obj, System::Windows::Forms::PaintEventArgs ^e) {
Graphics ^g = e->Graphics;
g->PixelOffsetMode = PixelOffsetMode::HighSpeed;
float by_x, by_y;
by_x = gameBoard.getByX();
by_y = gameBoard.getByY();
Image ^BoardImg = Image::FromFile(L"graphics\\gameboard.png");
g->DrawImageUnscaled(BoardImg,(int)(gameBoard.getByX()),(int)(gameBoard.getByY());
int count = Players.getCount();
if(count > 0) {
for(int i = 0; i<count; i++) {
int toy = Players.getToy(i);
switch(toy) {
case 1:
Image ^pImg = Image::FromFile(L"img\\shuttle.png");
break;
case 2:
Image ^pImg = Image::FromFile(L"img\\probe.png");
break;
case 3:
// etc... insert all pieces
break;
}
g->DrawImage(pImg,(int)((Players.getPosX(i)-1000)/by_x),
(int)((Players.getPosY(i)-1000)/by_y),(int)(2000.0f/by_x),(int)(2000.0f/by_y));
}
}
接下来将设计 16 张机会卡和 16 张公共财富卡的算法。在两个类中,将存储所有可能的卡片值。在每场游戏开始时,卡片会加载到两个数组中并洗牌,因此没有两场游戏会有相同的卡片序列。使用随机数生成器,值 1-16 将分配给每个牌组中的每张卡片。这将是每个牌堆的数字顺序。棘手的部分是确保在洗牌过程中,同一牌组中没有两张卡片被分配相同的数字值。为此,我们使用 Fisher-Yates 洗牌算法来确保这种情况不会发生。算法示例代码:
int arrayOrder[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16 };
Random rand = new Random();
for (int i = arrayOrder.Length - 1; i >= 0; i--) {
int r = rand.Next(0, i+1);
int temp = arrayOrder[i];
arrayOrder[i] = array[r];
arrayOrder[r] = temp;
}
for(int i = 0; i <= deck1->Count; i++) {
static_cast<Decks^>(deck1[i])->setOrder(arrayOrder[i]);
}
在编码时,重要的是要牢记未来的实现。这是为自己铺平道路并留下开放选择的最佳时机。通过将所有内容编码到类中,在游戏稳定发布第一个版本后,通过 JSON 序列化将此游戏更新为多人网络游戏将更容易实现。
Objects 类将用于创建每一张牌。使用 ArrayList,我可以创建两副独立的牌,每副 16 张。Decks 类存储牌的类型、在牌堆中的位置以及将显示的消息等信息。牌可以被读取、放回或从牌堆中删除。会保留剩余牌的数量。从 ArrayList 中移除一张牌很可能意味着这张牌被移动到其中一个玩家的拥有中(例如“出狱卡”),在这种情况下,另一个 Players 类将保存该信息以及该玩家的所有房产/金钱。以下是 Decks 类在代码中如何处理的示例:
ref class Decks { private: int card; int order; String ^msg; public: Decks() { Clear(); } Decks(int _card, int _order, String ^_msg) { set(_card,_order,_msg); } void set(int _card, int _order, String ^_msg) { card = _card; order = _order; msg = _msg; } int getCard() { if(this == nullptr) return -1; return card; } int getOrder() { if(this == nullptr) return -1; return order; } String ^getMsg() { if(this == nullptr) return L""; return msg; } void setOrder(int x) { order = x; } void Clear() { if(this == nullptr) return; card = -1; order = -1; msg = gcnew String(L""); } }; ref class Objects { private: ArrayList ^deck1; ArrayList ^deck2; public: Objects() { Clear(); } void addToDeck1(int card, int order, String ^msg) { deck1->Add(gcnew Decks(card,order,msg)); } void addToDeck2(int card, int order, String ^msg) { deck2->Add(gcnew Decks(card,order,msg)); } void removeFromDeck1(String ^msg) { for(int i = 0; i<deck1->Count; i++) { if(msg->Equals(static_cast<String^>(static_cast<Decks^>(deck1[i])->getMsg()))) { deck1->RemoveAt(i); break; } } } void removeFromDeck2(String ^msg) { for(int i = 0; i<deck2->Count; i++) { if(msg->Equals(static_cast<String^>(static_cast<Decks^>(deck2[i])->getMsg()))) { deck2->RemoveAt(i); break; } } } int getDeck1Count() { return deck1->Count; } int getDeck2Count() { return deck2->Count; } Decks ^getCardFromDeck1(int index) { if(index+1 > deck1->Count) return nullptr; return static_cast<Decks^>(deck1[index]); } Decks ^getCardFromDeck2(int index) { if(index+1 > deck2->Count) return nullptr; return static_cast<Decks^>(deck2[index]); } void Clear() { deck1 = gcnew ArrayList(); deck2 = gcnew ArrayList(); } };
Windows 触摸输入事件处理程序用于将您所需的效果链接到点击或滑动操作。玩家在掷骰子后可以选择在游戏板上滑动他们的棋子。Windows 8 内置了许多基本功能,因此鼠标点击事件等应该适用于在 Windows 8 中运行的桌面应用程序,但要执行精确功能,您需要将事件指向触摸输入。在以下示例中,我将演示我使用“平移”手势在游戏板上移动游戏棋子的方法(请记住,使用不同的输入方法有不止一种方法可以做到这一点)。
GESTUREINFO gi; bool isHandled = false; Point ^first = gcnew Point(); Point ^second = gcnew Point(); Players ^CurrentHero = Players.getActive(); switch(gi.dwID) { case GID_PAN: switch(gi.dwFlags) { case GF_BEGIN: first.X = gi.ptsLocation.x; first.Y = gi.ptsLocation.y; CurrentHero.highlight(first.X, first.Y); Players.CurrentHero = PointToClient(gcnew Point(first.X, first.Y) ); isHandled = true; break; case GF_END: PlayLoop(false); isHandled = true; case default: second.X = gi.ptsLocation.x; second.Y = gi.ptsLocation.y; CurrentHero.move(second.X,second.Y); Players.CurrentHero = PointToClient(gcnew Point(second.X, second.Y) ); isHandled = true; Invalidate(); first = second; break; } case default: isHandled = false; }
用户角应用程序将使用 OpenFL 框架开发,以支持 iOS 和 Android 设备。英特尔的通用连接框架中间件使智能手机能够与 Windows 操作系统通信。如果智能手机和计算机在同一网络中,则可以实现 Wifi 连接。只需配对设备即可实现蓝牙连接。
兴趣点
- 针对 2013 年英特尔应用创新大赛一体机平台游戏类别
- 使用线程绘制和运行游戏逻辑
- 使用对象类,序列化类将更容易实现多人网络
- 使用 Windows 8 API 集成触摸功能和鼠标点击事件以实现向后兼容性
- AIO 平台的大小非常适合作为游戏板。过去,在 PC 上与多名玩家玩经典棋盘游戏令人沮丧,仅仅是因为 PC 的尺寸(更不用说它都显示在立式显示器上)。将 AIO 平放允许多名用户舒适地围坐在游戏板周围。
- 新的电子骰子技术增强了玩经典棋盘游戏的用户体验
- 多用户多点触控技术允许指定的银行家进行银行交易,即使另一位玩家正在进行他/她的回合。每个用户在屏幕的任一边缘都有一个指定区域,用于查看他们的银行、房产和其他此类统计信息,这被称为“用户角”(银行家在他们的用户角中拥有银行特权)。
- 用户角将利用英特尔的 CCF 中间件实现多设备功能,使其可以通过智能手机访问。与智能手机的连接可以通过 Wifi 或蓝牙实现。
- 在线多人模式将成为可能,因此并非所有玩家都必须亲自在场(用户角仅向本地机器上的用户显示)。
历史
- 2013年9月1日:这是文章和所有已创建代码的初稿。
- 2013年9月2日:添加了类代码的延续,以处理两个牌组和用于洗牌的算法。
- 2013年9月3日:添加了触摸输入如何链接到游戏棋子移动的示例,更多关于游戏主题的背景信息,以及用户体验的预期。
- 2013年9月4日:添加了关于与智能手机一起使用的用户角应用程序的信息。