游戏邦在:
杂志专栏:
gamerboom.com订阅到鲜果订阅到抓虾google reader订阅到有道订阅到QQ邮箱订阅到帮看

如何创造一款简单的roguelikes游戏

发布时间:2014-02-01 08:39:15 Tags:,,,,

作者:Ido Yehieli

最近,roguelikes游戏成为了公众关注的焦点,像《地下城冒险》,《洞穴探险》,《以撒的结合》以及《FTL》都吸引了广泛用户的注意并受到了一致的好评。因为长期在小小的利基领域迎合着硬核玩家们,基于各种组合的roguelikes游戏元素现在能够将更多深度与重玩价值带到许多现有的游戏类型中了。

在本篇教程中,你将学习如何使用JavaScript以及HTML5游戏引擎Phaser创造一款传统的roguelikes游戏。最后,你将创造出一款带有完整功能的简单roguelikes游戏,并且能够运行于你自己的浏览器上!

roguelikes(from tutsplus)

roguelikes(from tutsplus)

注:尽管本篇文章的代码是使用JavaScript,HTML以及Phaser,你也应该能够使用其它代码语言和游戏引擎的技术和理念。

准备开始

对于本教程,你将需要一个文本编辑器和一个浏览器。我使用了Notepad++,虽然我更倾向于Google Chrome(因为它带有大量的开发工具),但工作流程将与你选择的任何文本编辑器和浏览器差不多。

然后你需要下载源文件并从初始文件夹开始;这包含了Phaser,基本的HTML以及我们游戏的JS文件。我们将在现在空空的rl.js文件中编写游戏代码。

index.htm文件将加载Phaser和我们之前提到的游戏代码文件:

<!DOCTYPE html>
<head>
<title>roguelike tutorial</title>
<script src=”phaser.min.js”></script>
<script src=”rl.js”></script>
</head>
</html>

初始化和定义

现在,我将为我们的roguelikes游戏使用ASCII图像——未来,我们可以用一些点阵图替代这些图像,但现在我们只需要使用ASCII让一切变得简单些。

让我们为字体大小,地图的维度以及上面有多少演员定义一些常量:

// font size
var FONT = 32;

// map dimensions
var ROWS = 10;
var COLS = 15;

// number of actors per level, including player
var ACTORS = 10;

让我们同样初始化Phaser并监听键盘的敲打事件,因为我们将创造一款回合制游戏并想要基于每次击键执行一次:

// initialize phaser, call create() once done
var game = new Phaser.Game(COLS * FONT * 0.6, ROWS * FONT, Phaser.AUTO, null, {
create: create
});

function create() {
// init keyboard commands
game.input.keyboard.addCallbacks(null, null, onKeyUp);
}

function onKeyUp(event) {
switch (event.keyCode) {
case Keyboard.LEFT:

case Keyboard.RIGHT:

case Keyboard.UP:

case Keyboard.DOWN:

}
}

因为默认等距字体的宽度是高度的60%,我们已经将canvas的大小初始化为0.6*字体大小*列数。我们同样也让Phaser在完成初始化后立刻调用我们的create()函数,届时我们将初始化键盘控制。

地图

平板图代表我们的游戏领域:分离的2D砖块数组,或单元格,均由一个ASCII字符呈现出来,它们也能够代表一堵墙或一个地板:

// the structure of the map
var map;

让我们使用最简单的程序生成形式去创造我们的地图:随机决定哪个单元格应该包含一堵墙或一个地板:

function initMap() {
// create a new random map
map = [];
for (var y = 0; y < ROWS; y++) {
var newRow = [];
for (var x = 0; x < COLS; x++) {
if (Math.random() > 0.8)
newRow.push(‘#’);
else
newRow.push(‘.’);
}
map.push(newRow);
}
}

这将呈现给我们一张80%的单元格是墙而剩下的是地板的地图。

在设置了键盘事件监听器后,我们在create()函数为游戏初始化新地图:

function create() {
// init keyboard commands
game.input.keyboard.addCallbacks(null, null, onKeyUp);

// initialize map
initMap();
}

屏幕

是时候绘制我们的地图了!我们的屏幕将是基于文本元素的2D数组,每个数组包含单一字符:

// the ascii display, as a 2d array of characters
var screen;

绘制地图将用地图的数值填充屏幕内容,因为它们都是简单的ASCII字符:

function drawMap() {
for (var y = 0; y < ROWS; y++)
for (var x = 0; x < COLS; x++)
screen[y][x].content = map[y][x];
}

最后,在我们绘制地图前我们必须初始化屏幕。让我们回到create()函数:

function create() {
// init keyboard commands
game.input.keyboard.addCallbacks(null, null, onKeyUp);

// initialize map
initMap();

// initialize screen
screen = [];
for (var y = 0; y < ROWS; y++)
var newRow = [];
screen.push(newRow);
for (var x = 0; x < COLS; x++)
newRow.push( initCell(”, x, y) );
}
drawMap();
}

function initCell(chr, x, y) {
// add a single cell in a given position to the ascii display
var style = { font: FONT + “px monospace”, fill:”#fff”};
return game.add.text(FONT*0.6*x, FONT*y, chr, style);
}

现在当你运行项目时将看到一个随机的地图呈现出来。

演员

接下来便是演员:我们的玩家角色,以及他们必须打败的敌人。每个演员都将是伴随着三个领域的对象:x和y代表他在地图上的位置,hp是名重点。

我们确保所有的演员都是在actorList数组中(第一个元素是玩家)。我们同样也与演员位置相联的阵列作为快速搜索键,如此我们便不需要为了找到处于某一特定位置的演员而迭代整个演员列表;当我们在编写移动和战斗代码时这种方法便非常有帮助。

// a list of all actors; 0 is the player
var player;
var actorList;
var livingEnemies;

// points to each actor in its position, for quick searching
var actorMap;

我们创造力所有演员,并为每个演员在地图上分配一个随机的位置:

function randomInt(max) {
return Math.floor(Math.random() * max);
}

function initActors() {
// create actors at random locations
actorList = [];
actorMap = {};
for (var e=0; e<ACTORS; e++) {
// create new actor
var actor = { x:0, y:0, hp:e == 0?3:1 };
do {
// pick a random position that is both a floor and not occupied
actor.y=randomInt(ROWS);
actor.x=randomInt(COLS);
} while ( map[actor.y][actor.x] == ‘#’ || actorMap[actor.y + "_" + actor.x] != null );

// add references to the actor to the actors list & map
actorMap[actor.y + "_" + actor.x]= actor;
actorList.push(actor);
}

// the player is the first actor in the list
player = actorList[0];
livingEnemies = ACTORS-1;
}

是时候呈现我们的演员了!我们将基于e绘制所有敌人,而基于命中点数绘制玩家角色:

function drawActors() {
for (var a in actorList) {
if (actorList[a].hp > 0)
screen[actorList[a].y][actorList[a].x].content = a == 0?”+player.hp:’e';
}
}

我们利用刚刚编写的函数并在create()函数中绘制所有演员:

function create() {

// initialize actors
initActors();

drawActors();
}

现在我们可以在关卡上看到我们的玩家角色和敌人了!

阻塞和适宜步行的砖块

我们需要确保我们的演员不会跑出屏幕外并穿越墙壁,所以让我们添加这一简单的检查去明确特定演员应该朝着哪一方向前进:

function canGo(actor,dir) {
return  actor.x+dir.x >= 0 &&
actor.x+dir.x <= COLS – 1 &&
actor.y+dir.y >= 0 &&
actor.y+dir.y <= ROWS – 1 &&
map[actor.y+dir.y][actor.x +dir.x] == ‘.’;
}

移动和战斗

我们已经到达一些互动:移动和战斗!在经典的roguelikes游戏中,基本的攻击是因为移向另一个演员而触发的,我们在moveTo()函数中同时处理了它们:

function moveTo(actor, dir) {
// check if actor can move in the given direction
if (!canGo(actor,dir))
return false;

// moves actor to the new location
var newKey = (actor.y + dir.y) +’_’ + (actor.x + dir.x);
// if the destination tile has an actor in it
if (actorMap[newKey] != null) {
//decrement hitpoints of the actor at the destination tile
var victim = actorMap[newKey];
victim.hp–;

// if it’s dead remove its reference
if (victim.hp == 0) {
actorMap[newKey]= null;
actorList[actorList.indexOf(victim)]=null;
if(victim!=player) {
livingEnemies–;
if (livingEnemies == 0) {
// victory message
var victory = game.add.text(game.world.centerX, game.world.centerY, ‘Victory!\nCtrl+r to restart’, { fill : ‘#2e2′, align: “center” } );
victory.anchor.setTo(0.5,0.5);
}
}
}
} else {
// remove reference to the actor’s old position
actorMap[actor.y + '_' + actor.x]= null;

// update position
actor.y+=dir.y;
actor.x+=dir.x;

// add reference to the actor’s new position
actorMap[actor.y + '_' + actor.x]=actor;
}
return true;
}

基本上:

1.我们确保演员尝试着移向一个适当的位置。

2.如果在这个位置上有另一个演员,我们会攻击他(如果他的HP到达0时将其杀死)。

3.如果在一个新的位置上没有另外的演员,我们便会移动到那。

需要注意的是当最后一个敌人被杀死时,我们也会呈现一个简单的胜利信息,并根据我们是否执行了有效的移动而返回正确或错误的信息。

现在,让我们回到onKeyUp()函数,改变它,从而让每次用户按压一个按键时,我们能够从屏幕上清楚之前演员的位置,并移动玩家角色到新位置,然后重新绘制演员:

function onKeyUp(event) {
// draw map to overwrite previous actors positions
drawMap();

// act on player input
var acted = false;
switch (event.keyCode) {
case Phaser.Keyboard.LEFT:
acted = moveTo(player, {x:-1, y:0});
break;

case Phaser.Keyboard.RIGHT:
acted = moveTo(player,{x:1, y:0});
break;

case Phaser.Keyboard.UP:
acted = moveTo(player, {x:0, y:-1});
break;

case Phaser.Keyboard.DOWN:
acted = moveTo(player, {x:0, y:1});
break;
}

// draw actors in new positions
drawActors();
}

我们很快便能使用所运行的变量而判断在每个玩家输入时敌人是否应该行动。

victory(from tutsplus)

victory(from tutsplus)

基本的人工智能

现在我们的玩家角色能够移动并发动攻击,让我们根据非常简单的路径发现让敌人在玩家离自己六步之内的距离时行动起来。(游戏邦注:如果玩家与敌人的距离较远,敌人便可以随机行走。)

需要注意的是我们的攻击代码并未考虑到哪个演员正在发动攻击,这便意味着如果你对其进行适当的排列,敌人将在尝试着追踪玩家角色的时候相互攻击,就像《毁灭战士》那样!

function aiAct(actor) {
var directions = [ { x: -1, y:0 }, { x:1, y:0 }, { x:0, y: -1 }, { x:0, y:1 } ];
var dx = player.x – actor.x;
var dy = player.y – actor.y;

// if player is far away, walk randomly
if (Math.abs(dx) + Math.abs(dy) > 6)
// try to walk in random directions until you succeed once
while (!moveTo(actor, directions[randomInt(directions.length)])) { };

// otherwise walk towards player
if (Math.abs(dx) > Math.abs(dy)) {
if (dx < 0) {
// left
moveTo(actor, directions[0]);
} else {
// right
moveTo(actor, directions[1]);
}
} else {
if (dy < 0) {
// up
moveTo(actor, directions[2]);
} else {
// down
moveTo(actor, directions[3]);
}
}
if (player.hp < 1) {
// game over message
var gameOver = game.add.text(game.world.centerX, game.world.centerY, ‘Game Over\nCtrl+r to restart’, { fill : ‘#e22′, align: “center” } );
gameOver.anchor.setTo(0.5,0.5);
}
}

我们已经添加了游戏结束信息,如果其中一个敌人杀死了玩家该信息便会呈现出来。

现在我们只剩下让敌人在玩家每次移动时进行移动,这要求我们在新位置上绘制演员前将如下代码添加到我们的onKeyUp()函数的最后:

function onKeyUp(event) {

// enemies act every time the player does
if (acted)
for (var enemy in actorList) {
// skip the player
if(enemy==0)
continue;

var e = actorList[enemy];
if (e != null)
aiAct(e);
}

// draw actors in new positions
drawActors();
}

gameover(from tutsplus)

gameover(from tutsplus)

额外提供:Haxe版本

我最初是在Haxe(游戏邦注:编译成JavaScript的一种优秀多平台语言)上编写这篇教程。尽管为了获得独特的JavaScript,我手动翻译了上述版本,但如果你和我一样更喜欢Haxe的话,你可以在源下载的haxe文件夹中找到Haxe版本。

你需要先安装一个haxe编译器,并使用任何文本编辑器,然后通过调用haxe build.hxml或双击build.hxml文件编译haxe代码。如果你更喜欢IDE的话我也添加一个FlashDevelop项目,你只需要打开rl.hxproj并按压F5运行便可。

现在我们已经完成了一款简单的roguelikes游戏了,并带有随机的地图生成,移动,战斗,AI以及胜利与失败条件。

以下是你可以添加到游戏中的一些新功能:

多个关卡

升级

库存

消费品

装备

本文为游戏邦/gamerboom.com编译,拒绝任何不保留版权的转载,如需转载请联系:游戏邦

How to Make Your First Roguelike

By Ido Yehieli

Roguelikes have been in the spotlight recently, with games like Dungeons of Dredmor, Spelunky, The Binding of Isaac, and FTL reaching wide audiences and receiving critical acclaim. Long enjoyed by hardcore players in a tiny niche, roguelike elements in various combinations now help bring more depth and replayability to many existing genres.

In this tutorial, you will learn how to make a traditional roguelike using JavaScript and the HTML 5 game engine Phaser. By the end, you will have a fully-functional simple roguelike game, playable in your browser! (For our purposes a traditional roguelike is defined as a single-player, randomized, turn-based dungeon-crawler with permadeath.)

Note: Although the code in this tutorial uses JavaScript, HTML, and Phaser, you should be able to use the same technique and concepts in almost any other coding language and game engine.

Getting Ready

For this tutorial, you will need a text editor and a browser. I use Notepad++, and I prefer Google Chrome for its extensive developer tools, but the workflow will be pretty much the same with any text editor and browser you choose.

You should then download the source files and start with the init folder; this contains Phaser and the basic HTML and JS files for our game. We will write our game code in the currently empty rl.js file.

The index.html file simply loads Phaser and our aforementioned game code file:

<!DOCTYPE html>
<head>
<title>roguelike tutorial</title>
<script src=”phaser.min.js”></script>
<script src=”rl.js”></script>
</head>
</html>

Initialization and Definitions

For the time being, we’ll use ASCII graphics for our roguelike—in the future, we could replace these with bitmap graphics, but for now, using simple ASCII makes our lives easier.

Let’s define some constants for the font size, the dimensions of our map (that is, the level), and how many actors spawn in it:

// font size
var FONT = 32;

// map dimensions
var ROWS = 10;
var COLS = 15;

// number of actors per level, including player
var ACTORS = 10;

Let’s also initialize Phaser and listen for keyboard key-up events, as we will be creating a turn based game and will want to act once for every key stroke:

// initialize phaser, call create() once done
var game = new Phaser.Game(COLS * FONT * 0.6, ROWS * FONT, Phaser.AUTO, null, {
create: create
});

function create() {
// init keyboard commands
game.input.keyboard.addCallbacks(null, null, onKeyUp);
}

function onKeyUp(event) {
switch (event.keyCode) {
case Keyboard.LEFT:

case Keyboard.RIGHT:

case Keyboard.UP:

case Keyboard.DOWN:

}
}

Since default monospace fonts tend to be about 60% as wide as they are high, we’ve initialized the canvas size to be 0.6 * the font size * the number of columns. We’re also telling Phaser that it should call our create() function immediately after it’s finished initialising, at which point we initialize the keyboard controls.

You can view the game so far here—not that there’s much to see!

The Map

The tile map represents our play area: a discrete (as opposed to continuous) 2D array of tiles, or cells, each represented by an ASCII character that can signify either a wall (#: blocks movement) or floor (.: doesn’t block movement):

// the structure of the map
var map;

Let’s use the simplest form of procedural generation to create our maps: randomly deciding which cell should contain a wall and which a floor:

function initMap() {
// create a new random map
map = [];
for (var y = 0; y < ROWS; y++) {
var newRow = [];
for (var x = 0; x < COLS; x++) {
if (Math.random() > 0.8)
newRow.push(‘#’);
else
newRow.push(‘.’);
}
map.push(newRow);
}
}

This should give us a map where 80% of the cells are walls and the rest are floors.

We initialize the new map for our game in the create() function, immediately after setting up the keyboard event listeners:

function create() {
// init keyboard commands
game.input.keyboard.addCallbacks(null, null, onKeyUp);

// initialize map
initMap();
}

You can view the demo here—although, again, there’s nothing to see, as we haven’t rendered the map yet.

The Screen

It’s time to draw our map! Our screen will be a 2D array of text elements, each containing a single character:

// the ascii display, as a 2d array of characters
var screen;

Drawing the map will fill in the screen’s content with the map’s values, since both are simple ASCII characters:

function drawMap() {
for (var y = 0; y < ROWS; y++)
for (var x = 0; x < COLS; x++)
screen[y][x].content = map[y][x];
}

Finally, before we draw the map we have to initialize the screen. We go back to our create() function:

function create() {
// init keyboard commands
game.input.keyboard.addCallbacks(null, null, onKeyUp);

// initialize map
initMap();

// initialize screen
screen = [];
for (var y = 0; y < ROWS; y++)
var newRow = [];
screen.push(newRow);
for (var x = 0; x < COLS; x++)
newRow.push( initCell(”, x, y) );
}
drawMap();
}

function initCell(chr, x, y) {
// add a single cell in a given position to the ascii display
var style = { font: FONT + “px monospace”, fill:”#fff”};
return game.add.text(FONT*0.6*x, FONT*y, chr, style);
}

You should now see a random map displayed when you run the project.

Actors

Next in line are the actors: our player character, and the enemies they must defeat. Each actor will be an object with three fields: x and y for its location in the map, and hp for its hit points.

We keep all actors in the actorList array (the first element of which is the player). We also keep an associative array with the actors’ locations as keys for quick searching, so that we don’t have to iterate over the entire actor list to find which actor occupies a certain location; this will help us when we code the movement and combat.

// a list of all actors; 0 is the player
var player;
var actorList;
var livingEnemies;

// points to each actor in its position, for quick searching
var actorMap;

We create all our actors and assign a random free position in the map to each:

function randomInt(max) {
return Math.floor(Math.random() * max);
}

function initActors() {
// create actors at random locations
actorList = [];
actorMap = {};
for (var e=0; e<ACTORS; e++) {
// create new actor
var actor = { x:0, y:0, hp:e == 0?3:1 };
do {
// pick a random position that is both a floor and not occupied
actor.y=randomInt(ROWS);
actor.x=randomInt(COLS);
} while ( map[actor.y][actor.x] == ‘#’ || actorMap[actor.y + "_" + actor.x] != null );

// add references to the actor to the actors list & map
actorMap[actor.y + "_" + actor.x]= actor;
actorList.push(actor);
}

// the player is the first actor in the list
player = actorList[0];
livingEnemies = ACTORS-1;
}

It’s time to show the actors! We’re going to draw all the enemies as e and the player character as its number of hitpoints:

function drawActors() {
for (var a in actorList) {
if (actorList[a].hp > 0)
screen[actorList[a].y][actorList[a].x].content = a == 0?”+player.hp:’e';
}
}

We make use of the functions we just wrote to initialize and draw all actors in our create() function:

function create() {

// initialize actors
initActors();

drawActors();
}

We can now see our player character and enemies spread out in the level!

Blocking and Walkable Tiles

We need to make sure that our actors aren’t running off the screen and through walls, so let’s add this simple check to see in which directions a given actor can walk:

function canGo(actor,dir) {
return  actor.x+dir.x >= 0 &&
actor.x+dir.x <= COLS – 1 &&
actor.y+dir.y >= 0 &&
actor.y+dir.y <= ROWS – 1 &&
map[actor.y+dir.y][actor.x +dir.x] == ‘.’;
}

Movement and Combat

We’ve finally arrived at some interaction: movement and combat! Since, in classic roguelikes, the basic attack is triggered by moving into another actor, we handle both of these at the same spot, our moveTo() function, which takes an actor and a direction (the direction is the desired difference in x and y to the position the actor steps in):

function moveTo(actor, dir) {
// check if actor can move in the given direction
if (!canGo(actor,dir))
return false;

// moves actor to the new location
var newKey = (actor.y + dir.y) +’_’ + (actor.x + dir.x);
// if the destination tile has an actor in it
if (actorMap[newKey] != null) {
//decrement hitpoints of the actor at the destination tile
var victim = actorMap[newKey];
victim.hp–;

// if it’s dead remove its reference
if (victim.hp == 0) {
actorMap[newKey]= null;
actorList[actorList.indexOf(victim)]=null;
if(victim!=player) {
livingEnemies–;
if (livingEnemies == 0) {
// victory message
var victory = game.add.text(game.world.centerX, game.world.centerY, ‘Victory!\nCtrl+r to restart’, { fill : ‘#2e2′, align: “center” } );
victory.anchor.setTo(0.5,0.5);
}
}
}
} else {
// remove reference to the actor’s old position
actorMap[actor.y + '_' + actor.x]= null;

// update position
actor.y+=dir.y;
actor.x+=dir.x;

// add reference to the actor’s new position
actorMap[actor.y + '_' + actor.x]=actor;
}
return true;
}

Basically:

1.We make sure the actor is trying to move into a valid position.

2.If there is another actor in that position, we attack it (and kill it if its HP count reaches 0).

3.If there isn’t another actor in the new position, we move there.

Notice that we also show a simple victory message once the last enemy has been killed, and return false or true depending on whether or not we managed to perform a valid move.

Now, let’s go back to our onKeyUp() function and alter it so that, every time the user presses a key, we erase the previous actor’s positions from the screen (by drawing the map on top), move the player character to the new location, and then redraw the actors:

function onKeyUp(event) {
// draw map to overwrite previous actors positions
drawMap();

// act on player input
var acted = false;
switch (event.keyCode) {
case Phaser.Keyboard.LEFT:
acted = moveTo(player, {x:-1, y:0});
break;

case Phaser.Keyboard.RIGHT:
acted = moveTo(player,{x:1, y:0});
break;

case Phaser.Keyboard.UP:
acted = moveTo(player, {x:0, y:-1});
break;

case Phaser.Keyboard.DOWN:
acted = moveTo(player, {x:0, y:1});
break;
}

// draw actors in new positions
drawActors();
}

We will soon use the acted variable to know if the enemies should act after each player input.

Basic Artificial Intelligence

Now that our player character is moving and attacking, let’s even the odds by making the enemies act according to very simple path finding as long as the player is six steps or fewer from them. (If the player is further away, the enemy walks randomly.)

Notice that our attack code doesn’t care who the actor is attacking; this means that, if you align them just right, the enemies will attack each other while trying to pursue the player character, Doom-style!

function aiAct(actor) {
var directions = [ { x: -1, y:0 }, { x:1, y:0 }, { x:0, y: -1 }, { x:0, y:1 } ];
var dx = player.x – actor.x;
var dy = player.y – actor.y;

// if player is far away, walk randomly
if (Math.abs(dx) + Math.abs(dy) > 6)
// try to walk in random directions until you succeed once
while (!moveTo(actor, directions[randomInt(directions.length)])) { };

// otherwise walk towards player
if (Math.abs(dx) > Math.abs(dy)) {
if (dx < 0) {
// left
moveTo(actor, directions[0]);
} else {
// right
moveTo(actor, directions[1]);
}
} else {
if (dy < 0) {
// up
moveTo(actor, directions[2]);
} else {
// down
moveTo(actor, directions[3]);
}
}
if (player.hp < 1) {
// game over message
var gameOver = game.add.text(game.world.centerX, game.world.centerY, ‘Game Over\nCtrl+r to restart’, { fill : ‘#e22′, align: “center” } );
gameOver.anchor.setTo(0.5,0.5);
}
}

We’ve also added a game over message, which is shown if one of the enemies kills the player.

Now all that’s left to do is make the enemies act every time the player moves, which requires adding the following to the end of our onKeyUp() functions, right before drawing the actors in their new position:

function onKeyUp(event) {

// enemies act every time the player does
if (acted)
for (var enemy in actorList) {
// skip the player
if(enemy==0)
continue;

var e = actorList[enemy];
if (e != null)
aiAct(e);
}

// draw actors in new positions
drawActors();
}

Bonus: Haxe Version

I originally wrote this tutorial in a Haxe, a great multi-platform language that compiles to JavaScript (among other languages). Although I translated the version above by hand as to make sure we get idiosyncratic JavaScript, if, like me, you prefer Haxe to JavaScript, you can find the Haxe version in the haxe folder of the source download.

You need to first install the haxe compiler and can use whatever text editor you wish and compile the haxe code by calling haxe build.hxml or double-clicking the build.hxml file. I also included a FlashDevelop project if you prefer a nice IDE to a text editor and command line; just open rl.hxproj and press F5 to run.
Summary

That’s it! We now have a complete simple roguelike, with random map generation, movement, combat, AI and both win and lose conditions.

Here are some ideas for new features you can add to your game:

multiple levels

power ups

inventory

consumables

equipment

Enjoy!(source:tutsplus)


上一篇:

下一篇: