游戏事件系统(Game Event/Message System)

  当游戏里面的元素多起来后,对象之间产生的事件将会越发复杂,比如主角走路,会对周围所有的角色发送当前坐标信息;比如主角打怪,除了通知其他人打怪的状态信息,还要通知怪物,您被虐了;还有很多都可以用事件来做,操作系统的事件驱动的方式就明显优于轮询方式。事件系统相当于将所有这些事件统一起来,让开发者易于扩展及维护。

 

  一、简单的事件系统

  参考:

  http://www.gamedev.net/reference/articles/article2141.asp

 

 直接看代码IEventHandler.h

 

  1. 首先定义事件结构体:

  // The event structure
struct Event {
  int Type;
  int arg1, arg2;
};

 

 一个类型,加上两个参数。(越简单越好,能阐述问题)

 

  2. 为了type好辨认,通过enum管理所有的消息类型

 

  // Defination of all the different type of events
enum EventType
{
   // Mouse button events. Each of the next six events gets passed
  // the absolute position of the mouse
  E_MOUSELEFTBUTTONPRESS,
  E_MOUSELEFTBUTTONRELEASE,

  // Start new games
  E_NEWGAMEEASY,
  E_NEWGAMENORMAL,
  E_NEWGAMEHARD,

  // Game play related
  E_INCREMENTSCORE,
  E_PAUSEGAME,
  E_CONTINUEGAME,
  E_GAMEOVER,

  // Close the App
  E_APPCLOSE
};

 

还是很简单,鼠标键盘消息,游戏难度选项,游戏状态等

 

 3. 定义一个接口,事件处理器(EventHandler)

 

class IEventHandler {

public:
  virtual void EventHandler(const Event &e) = 0;   // 纯虚函数,继承类必须实现

  // Mutator and selector
  IEventHandler * GetNextHandler(void) {return _nextHandler;} 
  void SetNextHandler(IEventHandler *next) {_nextHandler = next;}


// 构造函数使得对象一创建就自动注册到了时间分发器中,因为消息的分发主要靠事件分发器

// 遍历所有的事件处理器来发送事件。 而分发器是单例的,相对于事件处理器是一对多的关系,

// 后面会接着分析

  IEventHandler() : _nextHandler(0) {

    EventDispatcher::Get()->RegisterHandler(this);
  }

protected:
  void SendEvent(int eventType, int arg1 = 0, int arg2 = 0) {
    EventDispatcher::Get()->SendEvent(eventType, arg1, arg2);
  }

private:
  IEventHandler *_nextHandler;   // 将会有多个事件处理器,这里用链表来实现
};

 

 4. 接着看EventDispatcher.h

 

class IEventHandler;  // 类的前向声明

 

class EventDispatcher {

// definitions necessary to handle a singleton class
public:
  //returns a pointer to the EventDispatcher singleton instance
  static EventDispatcher*    Get();  // 提供一个得到对象指针的机会 , 与外界沟通的桥梁

private:
  // These are private so that only instance will ever be created
  // the _deviceList pointer is initialized to null as the linked list
  // is null-terminated
  EventDispatcher(void) : _deviceList(0) {;}  // 构造函数私有化,不能用new来创建对象
  ~EventDispatcher(void) {;} // 析构函数私有化,不能在栈上创建对象 , 应该添加一个释放资源的函数,要不没机会释放了

  // pointer to singleton class
  static EventDispatcher*    _instanceOf;

// definitions of methods which handle events
public:

  void RegisterHandler(IEventHandler *device); // 注册事件处理器,放入链表

  // Sends the event to all the devices registered to listen
  void SendEvent(int eventType, int arg1 = 0, int arg2 = 0); // 遍历所有事件处理器,发送消息

private:
  IEventHandler *_deviceList; // 这里体现出来关系了,一对多。
};

 

  5. 最后来看看实现吧,EventDispatcher.cpp

 

#include "EventDispatcher.h"

#include "IEventHandler.h"

// pointer to singleton instance
EventDispatcher* EventDispatcher::_instanceOf = 0;  // 初始化单例 对象

 

// 通过这个方法来创建单例对象,有点太懒了^_^,多个函数都不愿意写

EventDispatcher* EventDispatcher::Get() {
    if (_instanceOf)
        return _instanceOf;
    return _instanceOf = new EventDispatcher();
}

// 链表
void EventDispatcher::RegisterHandler(IEventHandler *device) {
  device->SetNextHandler(_deviceList);
  _deviceList = device;
}


void EventDispatcher::SendEvent(int eventType, int arg1, int arg2) {
  Event e;
  e.Type = eventType;
  e.arg1 = arg1;
  e.arg2 = arg2;
  IEventHandler * curDevice = _deviceList;

  // 遍历
  for (; curDevice; curDevice = curDevice->GetNextHandler())
    curDevice->EventHandler(e);
}

 

6. 使用 main.cpp

 

// 继承接口,实现虚函数

class A : public IEventHandler {

public:

  // 真正处理消息的函数,可以只针对相应的类型

  void EventHandler(const Event &e) {
    switch (e.Type) {
      case E_NEWGAMEEASY:
        cout << "Class A handling E_NEWGAMEEASY event" << endl;
        cout << "Class A sending a E_INCREMENTSCORE event" << endl;
        SendEvent(E_INCREMENTSCORE);
        break;
      case E_PAUSEGAME:
        cout << "Class A handling E_PAUSEGAME event" << endl;
        break;
    }
  }
};

 

class B : public IEventHandler {

public:
  void EventHandler(const Event &e) {
    switch (e.Type) {
      case E_INCREMENTSCORE:
        cout << "Class B handling E_INCREMENTSCORE event" << endl;
        break;
      case E_PAUSEGAME:
        cout << "Class B handling E_PAUSEGAME event" << endl;
        break;
    }
  }
};


void main () {
  A a1; // 定义就被放入了分发器中
  B b1;

  cout << "Main fct sending E_NEWGAMEEASY event" << endl;
  EventDispatcher::Get()->SendEvent(E_NEWGAMEEASY); // 直接发送消息,群发
  cout << "Main fct sending E_PAUSEGAME event" << endl;
  EventDispatcher::Get()->SendEvent(E_PAUSEGAME);

  char c;
  cout << "Press any key followed by [Enter] to exit" << endl;
  cin >> c;

  exit(0);
}

 

是不是有很多想法,那就对了,接着写吧。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
package com.game.gobang; import javax.imageio.ImageIO; import javax.swing.; import java.awt.; import java.awt.event.*; import java.awt.image.BufferedImage; import java.io.File; import java.net.URLEncoder; import java.util.Objects; public class GameFrame extends JFrame implements ActionListener { /** 游戏面板 / private GamePanel gamePanel; /* * 功能:构造函数<br> / public GameFrame() { try { JMenuBar jmb = new JMenuBar(); JMenu jm_game = new JMenu("菜单"); jm_game.setFont(new Font("微软雅黑",Font.PLAIN,12)); JMenuItem jmi_game_new = jm_game.add("新游戏"); jmi_game_new.setFont(new Font("微软雅黑",Font.PLAIN,12)); jmi_game_new.addActionListener(this); jmi_game_new.setActionCommand("new"); jmb.add(jm_game); JMenu jm_help = new JMenu("帮助"); jm_help.setFont(new Font("微软雅黑",Font.PLAIN,12)); JMenuItem jmi_help_about = jm_help.add("游戏规则"); jmi_help_about.setFont(new Font("微软雅黑",Font.PLAIN,12)); jmi_help_about.addActionListener(this); jmi_help_about.setActionCommand("about"); JMenuItem jmi_help_about1 = jm_help.add("截图"); jmi_help_about1.setFont(new Font("微软雅黑",Font.PLAIN,12)); jmi_help_about1.addActionListener(this); jmi_help_about1.setActionCommand("about1"); jmb.add(jm_help); this.setJMenuBar(jmb); //面板 this.gamePanel = new GamePanel(); this.add(this.gamePanel); //显示 this.setTitle("五子棋"); this.setLayout(null); this.setSize(760,680); this.setResizable(false); this.setLocationRelativeTo(null); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setVisible(true); } catch(Exception e) { JOptionPane.showMessageDialog(this,"程序出现异常错误,即将退出!\r\n\r\n","提示",JOptionPane.ERROR_MESSAGE); System.exit(0); } } /* * 功能:事件监听<br> */ public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); if("new".equals(command)) { this.gamePanel.newGame(); } else if("about".equals(command)) { JOptionPane.showMessageDialog(this,"游戏胜利条件:五颗白棋或黑棋相连","提示",JOptionPane.INFORMATION_MESSAGE); } else if("about1".equals(command)) { } } } 在这段代码里添加截图功能,能自己命名文件,并且保证可以截图后不会替换上一张的截图。
05-25

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值