Some reminiscences, some memories

Just another boring day
« 服务器又休息了两天
《围墙》——钱终输 »

22

May

在游戏中使用脚本语言

mikespook 

这只是一个有趣的探索,demo 使用 java 编写。模拟了一个龙与地下城类 RPG 游戏中,在不同的房间内移动的简单游戏场景。

阅读本文前,首先下载使用 Netbeans 6.5 建立的完整项目代码:下载。然后,我会用 UML 图的方式来说明如何在游戏中使用脚本,其中可能还会简介一下游戏中实体对象的建立和管理(不知道值得不值得另外写一篇文章来介绍了)。

I have a dream…

现在要构建一个极为无聊的小世界。说它小,是因为我只打算让它有三个房间,三个房间之间两两互通的门。仅此而已。首先上图:三个房间

我们假设左边上面的房间叫 room-1,右边上面的房间叫 room-2,下面的房间叫 room-3。玩家在这三个房间中穿行,当然,不可能是穿墙。人,一定是要走门的。如图。就这么简单的逻辑而已,不用脚本语言也能轻松完成。不过如果希望多来一点拓展性呢?比如,room-3 不允许等级在5级以下玩家进入;room-2 当十级以上玩家进入后就会自动瞬间移动到 room-1;门锁住以后就不能通过,更夸张一点,门锁住以后如果不把锁打坏就不能通过……可能性太多了。不用脚本的情况下,如果要将这么多都实现,是一件非常繁琐的事情。

好吧,让我们来看看那些游戏公司是怎么解决这个问题的。哦,需要说明的是,这里的解决方案仅仅是一个 demo,只用来解释原理。真正的环境中,还要更复杂一点,不过也就是复杂一点点而已。

类图游戏中实体类如图设计,为了简化期间,没有实体管理器,所以也没有集成自统一的父类。

Ninny 类也就是玩家,有保存当前所在房间的成员变量。Room 类也就是房间,保存有在当前房间的玩家列表。Door 是描述门的类,保存了这个门连接的两个房间的列表。

为了简单期间,demo 中使用了 java 内置的 javascript 作为脚本语言,详情看这里。

本来还想多写一点的,急着出门。算了,反正有代码,大家看看先。有空回头写。重点在 js 目录下的那几个脚本 enterDoor.js,exitDoor.js,enterRoom.js,exitRoom.js。

好,继续!

为了用起来方便,稍稍封装了一下Scripting的代码。大家直接看代码,不说话:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
 
public class Script {
 
    private final static ScriptEngineManager factory = new ScriptEngineManager();
    private final static ScriptEngine engine = factory.getEngineByName("JavaScript");
 
    public void put(String key, Object value) {
        engine.put(key, value);
    }
 
    public Object get(String key) {
        return engine.get(key);
    }
 
    public Object eval(String fileName) throws ScriptException {
        try {
            return engine.eval(new FileReader(System.getProperty("user.dir", ".") + "/js/" + fileName + ".js"));
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Script.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }
}

从类图上可以看到,Door 和 Room 都实现了 Enterable 接口,两个方法:一个入 enter,一个出 exit。显然,当进入一个 Room 的时候,必须从另外一个房间出来。为了保证这个一致性,所以限定玩家不能穿墙只能走门:

1
2
3
4
5
6
7
        Room r1 = new Room("room-1");
        Room r2 = new Room("room-2");
        Door d12 = new Door();
        d12.add(r1);
        d12.add(r2);
        Ninny player = new Ninny("ninny", r1);
        d12.enter(player);

在不用脚本的情况下怎么写这个 Door 的 enter 方法呢?首先判断一下 ninny 是不是跟 Door 在同一个房间;获得 ninny 的当前房间后 exit;获得门另一端的房间 enter。就是这么简单。不过考虑到前面说说的那种种可能性,为了拓展让我们来看一看用脚本是如何处理的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Door 的 enter 方法
    public boolean enter(Ninny ninny) {
        Room currentRoom = ninny.getOwner();
        // 玩家在当前门所在的房间
        if(!member.containsKey(currentRoom.getName())) {
            return false;
        }
        // 门另一侧的房间
        Room nextRoom = getAnotherRoom(currentRoom);
 
        try {
            Script script = new Script();
            script.put("room1", currentRoom);
            script.put("room2", nextRoom);
            script.put("door", this);
            script.put("player", ninny);
            script.eval("enterDoor");
            System.out.println("enterDoor: " + script.get("message"));
            return Boolean.valueOf(script.get("result").toString());
        } catch (ScriptException ex) {
            Logger.getLogger(Door.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }        
    }

这里实际上 enter 什么也没有做,只是传递了一些数据到 Script,然后执行了 enterDoor 这个脚本:

1
2
3
4
5
6
7
8
9
importClass(Packages.foobar.Room);
importClass(Packages.foobar.Ninny);
importClass(Packages.foobar.Door);
 
room1.exit(player);
room2.enter(player);
 
var message = player.getName() + " enter from the room " + room1.getName() +  " to the romm " + room2.getName() + "!";
var result = true;

脚本也很简单,只是让玩家推出当前房间,进入下一个房间。如果房间对进入的玩家有等级要求,则只需:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
importClass(Packages.foobar.Room);
importClass(Packages.foobar.Ninny);
importClass(Packages.foobar.Door);
 
var result = false;
var message = '';
if(room2.needLevel >= player.level ) {
    room1.exit(player);
    room2.enter(player);
 
    message = player.getName() + " enter from the room " + room1.getName() +  " to the romm " + room2.getName() + "!";
    result = true;
} else {
    message = player.getName() + " can't enter the romm " + room2.getName() + "! Level" + room2.needLevel +" needed!";
}

在我的例子代码中并没有这部分代码,实际上 Room、Door、Ninny 这几个类都应该从一个父类 Entity 中继承。这个 Entity 有一个 Map 的成员变量。通过 setAttr(String, Object) 和 getAttr(String) 两个方法就可以分别设置和获取每个 Entity 的属性。这样就可以在脚本中自由的使用各种属性来实现各种功能。
通过 Enterable(可进入),Pickable(可捡起),Attackable(可攻击)等接口,调用对应的脚本来完成真正的游戏逻辑。
其实那神秘的游戏脚本化就是这么简单。本来还想画几个序列图说明一下脚本的调用,实在有些困了。
准备洗洗睡觉。尚未补充完整的内容,全当大家进阶学习吧。

This entry was posted on Friday, May 22nd, 2009 at 05:55 and is filed under Game, NetBeans. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

4 Responses to “ 在游戏中使用脚本语言 ”

  1. # 1 小路 Says:
    May 22nd, 2009 at 16:07

    我觉得,若是java的话,大可不必这么做,有强悍的JRUBY,JPTYON,JPHP之类什么的,名字别在意,瞎扯的..

    直接公用java写好的类库,更强大方便,这不是也挺好的吗?

  2. # 2 隐藏人物 Says:
    May 23rd, 2009 at 03:08

    关键词是ninny~

  3. # 3 mikespook Says:
    May 23rd, 2009 at 05:07

    路神,兄弟!Jruby、Jytion、或者 Jphp 之类的都需要三方库,Javascript 的 Scripting 是 java 环境自带的,所以 javascript 是比其他更简单的解决方案。
    而且我这里有 Script 类,你大可封装其他的 Scripting 脚本语言来嵌入。Just a demo.

  4. # 4 ninny Says:
    May 26th, 2009 at 07:24

    关键词是 NEW ninny~

Leave a Reply

Trackback URI | Comments RSS

 

July 2010
M T W T F S S
« Jun    
 1234
567891011
12131415161718
19202122232425
262728293031  

Recent Comments

  • Rebill on 我们精通精通
  • 食品楼8 on ucweb 面试记
  • nio on 我们精通精通
  • Some reminiscences, some memories » Blog Archive » [翻译]Akihabara 指南,第四部分:地图的卷动 on [翻译]Akihabara 指南,第二部分:精灵的移动
  • Some reminiscences, some memories » Blog Archive » [翻译]Akihabara 指南,第四部分:地图的卷动 on [翻译]Akihabara 指南,第三部分:基本的地图

Tags

乱码 协程 地震 备案 安装 广州 异步 性能 我爱发明 扯淡 招聘 游戏 漏洞 翻译 豆瓣 39.com Adobe akihabara config countdown Demo Flash game engine golang google html5 issue linux Micromedia MongoDB mysql NetBeans nginx NoSQL oracle PHP phpunit Python trac ubuntu xdebug xml xubuntu yield Zend Framework

Blogroll

  • Blog on 27th Floor
  • DBA notes
  • Tim[后端技术]
  • 唐海燕
  • 抚琴居
  • 某人的栖息地
  • 番茄’s Blog
  • 網站製作學習誌
  • 纸老虎传媒

Old friends

  • cbf107

Only

  • 媚惑桃花

OurPNP

  • Biaoest:标的最高级
  • flexsns-官方博客
  • PNP University
  • PNP街坊
  • smallfish 鱼哥的窝子
  • 冰山日志
  • 墙外的光神V5
  • 夜雨's Blog
  • 小冬kobe
  • 小路的奇异世界
  • 广州萌芽工作室
  • 老甘blog
  • 赖勇浩的编程私伙局

Meta

  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org

Powered by WordPress
mikespook.com 粤ICP备09065095号