BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / java-script / #72同步于 2016/9/9
该镜像源已超过 30 天没有更新,可能在源站已被删除。
JavaScript机器人发帖

Java Scripting:Java里嵌入脚本语言

nuanyangyang
2016/9/9镜像同步21 回复
假如我在开发一个RPG游戏。游戏系统自然是很重要的一部分,但数值设计更重要。这涉及到各个角色的血量、攻击力、防御力等数值。为了灵活起见,我们希望这些数值可以很方便地调节。最好方便到不用重新编译系统,只要改一个文本文件,就可以修改数值。而且,我们的专业“数值设计师”是玩桌游的高手,设计过万智牌,拿过三国杀冠军,当过龙与地下城的城主,给农场主、小矮人游戏的作者提过意见,但编程经验并不丰富,甚至可以说几乎没有编程经验。 这种情况,就是脚本语言发挥它的特长的时间了。比如,我们可以让数值设计师编辑下面的文件: ```js // charSetting.js var Player = Packages.cn.byr.nuanyangyang.jstest.RolePlaying.Player; hero = new Player(); hero.name = "Warrior"; hero.health = 100; hero.damage = 30; hero.armor = 30; villain = new Player(); villain.name = "Monster"; villain.health = 120; villain.damage = 40; villain.armor = 10; ``` 先不管第一行。除此之外,后面的只要识字的就能看懂:我们创建了两个玩家,一个叫"Warrior",一个叫"Monster",它们的血量、攻击力、防御力如代码所示。程序员只要简单地告诉数值设计师应该如何编辑这个脚本,数值设计师就可以发挥他的特长,设计一个有挑战性的游戏了。 从程序员的角度看,上述脚本就是一个嵌入在一个大程序里的小程序,而“大程序”和“小程序”之间有这样的一个约定:“脚本执行完以后,全局变量`hero`和`villain`分别存着两个`Player`对象”。那么,“大程序”要做的,就是把“小程序”装载进来,用一个**脚本引擎**来执行它,然后获取那两个全局变量就行了。 下面是一个例子,用Java写的。Java很早就有标准的“Java Scripting”接口。注意,不是JavaScript,而是“给Java语言做Scripting,即在Java语言里嵌入脚本语言的接口”。Java程序员要做的就是创建一个ScriptEngine对象,然后装载这个脚本就行了。 ```java // RolePlaying.java package cn.byr.nuanyangyang.jstest; import java.io.BufferedReader; import java.nio.file.Files; import java.nio.file.Paths; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; /** 简单的RPG游戏 */ public class RolePlaying { /** 游戏角色 */ public static class Player { public String name; public int health; public int damage; public int armor; public void show() { System.out.printf("Character: %s\n", name); System.out.printf(" health = %d\n", health); System.out.printf(" damage = %d\n", damage); System.out.printf(" armor = %d\n", armor); } public boolean isAlive() { return health > 0.0; } } public static void main(String[] args) throws Exception { // 创建一个scripting engine用来解释JavaScript ScriptEngineManager sem = new ScriptEngineManager(); ScriptEngine engine = sem.getEngineByName("JavaScript"); // 角色的数值在js里配置 try (BufferedReader r = Files.newBufferedReader(Paths.get("rpg", "charSetting.js"))) { engine.eval(r); } // 两个角色分別在全局变量“hero”和“villain”里。 Player hero = (Player) engine.get("hero"); Player villain = (Player) engine.get("villain"); hero.show(); villain.show(); // 让它们战斗 fight(hero, villain); } private static void fight(Player player1, Player player2) { Player attacker = player1, defender = player2; // 轮流进攻 while (true) { // 直到一个角色死为止,战斗结束。 if (!player1.isAlive()) { System.out.printf("%s died.\n", player1.name); break; } else if (!player2.isAlive()) { System.out.printf("%s died.\n", player2.name); break; } // 打印当前状态 System.out.printf("%s:%d, %s:%d\n", player1.name, player1.health, player2.name, player2.health); // 攻击,计算伤害。简便起见,伤害的计算方法就是damage-armor。 int damageDealt = attacker.damage - defender.armor; defender.health -= damageDealt; System.out.printf(" %s hit %s and dealt %d damage\n", attacker.name, defender.name, damageDealt); // 攻防双方交换 Player tmp = attacker; attacker = defender; defender = tmp; } } } ``` 把上述java程序存成`cn/byr/nuanyangyang/jstest/RolePlaying.java`,把js脚本存成`rpg/charSetting.js`,编译运行就行了。输出结果应该是: ``` Character: Warrior health = 100 damage = 30 armor = 30 Character: Monster health = 120 damage = 40 armor = 10 Warrior:100, Monster:120 Warrior hit Monster and dealt 20 damage Warrior:100, Monster:100 Monster hit Warrior and dealt 10 damage Warrior:90, Monster:100 Warrior hit Monster and dealt 20 damage Warrior:90, Monster:80 Monster hit Warrior and dealt 10 damage Warrior:80, Monster:80 Warrior hit Monster and dealt 20 damage Warrior:80, Monster:60 Monster hit Warrior and dealt 10 damage Warrior:70, Monster:60 Warrior hit Monster and dealt 20 damage Warrior:70, Monster:40 Monster hit Warrior and dealt 10 damage Warrior:60, Monster:40 Warrior hit Monster and dealt 20 damage Warrior:60, Monster:20 Monster hit Warrior and dealt 10 damage Warrior:50, Monster:20 Warrior hit Monster and dealt 20 damage Monster died. ``` 这个机制很灵活。如果对这个战斗结果不满意,我会修改那个js,把monster的攻击力调成100。保存js,不用重新编译java,重新执行,战斗结果立马就不同了: ``` Character: Warrior health = 100 damage = 30 armor = 30 Character: Monster health = 120 damage = 100 armor = 10 Warrior:100, Monster:120 Warrior hit Monster and dealt 20 damage Warrior:100, Monster:100 Monster hit Warrior and dealt 70 damage Warrior:30, Monster:100 Warrior hit Monster and dealt 20 damage Warrior:30, Monster:80 Monster hit Warrior and dealt 70 damage Warrior died. ``` 对于大型游戏来说,游戏甚至不用退出,重新装载脚本,就可以运行了。 现实世界中,除了Java以外,很多人也会试图在C/C++里嵌入脚本。除了JS以外,Lua也是一个适合这种用途的语言。甚至Lua最初就是为数据录入而设计的。而游戏是Lua的最大的应用场景之一。 参考: 语言无关的通用Java Scripting接口: https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/toc.html Java 1.7开始自带的Nashorn JavaScript引擎: https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/toc.html
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
PiEgg机器人#1 · 2016/9/10
暖神的帖子居然没人顶,不科学
Forsun机器人#2 · 2016/9/10
Ncer机器人#3 · 2016/9/10
rpg maker的最新版用的语言已经从ruby改成js了
OnlySaturday机器人#4 · 2016/9/10
顶顶顶
nuanyangyang机器人#5 · 2016/9/10
【 在 Ncer 的大作中提到: 】 : rpg maker的最新版用的语言已经从ruby改成js了 这是为什么呢?更容易嵌入吗?这个我倒是同意。起码js有标准,也有嵌入式的引擎。
szm1002机器人#6 · 2016/9/10
不懂bd。。
cc19931002机器人#7 · 2016/9/10
于是我还是想知道js代码第一行是什么东东[ema13]
nuanyangyang机器人#8 · 2016/9/10
乖,读文档去 【 在 cc19931002 的大作中提到: 】 : 于是我还是想知道js代码第一行是什么东东
cc19931002机器人#9 · 2016/9/10
不知道该看什么文档。。。我仔细看了下,发现他第一行和那个java类的内部类好像有什么不可告人的秘密~ 【 在 nuanyangyang 的大作中提到: 】 : 乖,读文档去