内容


可以用 PHP 编写的 30 个游戏脚本,第 2 部分

开发 10 个中级脚本

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 可以用 PHP 编写的 30 个游戏脚本,第 2 部分

敬请期待该系列的后续内容。

此内容是该系列的一部分:可以用 PHP 编写的 30 个游戏脚本,第 2 部分

敬请期待该系列的后续内容。

在本系列的 第 1 部分 中,您创建了一些初学者级别的 PHP 脚本。现在是时候提高要求来增加一些复杂度,进一步拓展您的 PHP 编程技能并提高您作为游戏专家的地位。

对于角色扮演游戏脚本,您将了解如何整合武器的破坏力计算器、特征表统计跟踪器及非玩家角色(Non-Player Character,NPC)生成器,同时教您如何将信息保存到文件中以及如何与 PHP 中的数组结合使用。与机率游戏相关的脚本将帮助您练习 Blackjack,学习如何算牌,并且快速了解 Bingo(一种赌博游戏)的基本规则,同时教您如何将机率元素插入到 PHP 脚本中。最后,文字游戏脚本将帮助您解决 Jumble 游戏,创建简单的置换计算程序,并生成文字搜索图,同时让您的 PHP 数组处理技能更上一层楼。

我们将快速地介绍这些脚本,而不会讨论如何查找主机或设置服务器。有很多 Web 托管公司提供 PHP,并且如果需要安装自己的 PHP,XAMPP 安装程序使用起来也十分简单。我们将不会花费大量时间谈论 PHP 最佳实践或游戏设计技术。本文介绍的脚本易于理解、使用简单并可以快速掌握。

本文的 代码归档 包含所讨论的每个脚本的完整源代码,并且可以访问 Chaoticneutral.net 查看脚本实际运行情况。

破坏力计算器

第 1 部分中,我们构建了一个基本的掷骰器。让我们查看一下如何使用该掷骰器帮助整合基本的破坏力计算器。

如果您曾经运行或者玩过桌面角色扮演游戏,则可能已经看到过书中附带的大型武器图表。通常,这些图表包括武器名称及武器的破坏力有多大。它可能类似表 1 中所示的内容。

表 1. 破坏力计算器
武器名称破坏力
Little Stick1d6
Big Stick1d6+4
Chainsaw2d8

您应当能够得到在 “可以用 PHP 编写的 30 个游戏脚本” 系列的 第 1 部分 中创建的简单 roll 函数,并且使用它计算一套基本武器的破坏力。为此,需要创建一个数组来保存关于武器的信息。

对于本例,一个武器有三个基本特性:名称(Name)、掷骰(Die Roll)和伤害奖励(Damage Bonus)。使用这些特性,您可以构建武器数组,如下所示:

清单 1. 武器数组
$weapons = array (
    'littlestick' => array (
        'name' => 'Little Stick',
        'roll' => '1d6',
        'bonus' => '0',
    ),
    'bigstick' => array (
        'name' => 'Little Stick',
        'roll' => '1d6',
        'bonus' => '4',
    ),
    'chainsaw' => array (
        'name' => 'Little Stick',
        'roll' => '2d8',
        'bonus' => '0',
    ),
);

使用此数组,您可以构建一张显示武器、武器的破坏力及伤害判定结果表。

清单 2. 构建武器表
foreach ($weapons as $weapon) {
    list($count, $sides) = explode('d', $weapon['roll']);
    $result = 0;
    for ($i = 0; $i < $count;$i++) {
        $result = $result + roll($sides);
    }
    echo "<tr><td>" . $weapon['name'] . "</td><td>" 
. $weapon['roll'];
    if ($weapon['bonus'] > 0) {
        echo "+" . $weapon['bonus'];
        $result = $result + $weapon['bonus'];
    }
    echo "</td><td>" . $result . "</td></tr>";
}

使用此脚本,您可以用集成的破坏力计算器创建基本武器图。

统计跟踪

现在已经可以计算破坏力,让我们查看如何跟踪一些基本任务统计信息,这种功能在要运行带有多个 NPC 的游戏时可能需要使用。创建一个简单数组来保存人物信息并将其另存(序列化)为文本文件(将这些信息保存到数据库中有许多方法,并且在您逐渐熟悉如何使用 PHP 后应当保存到数据库)。对于本例,您将通过我编写的名为 Shambles 的游戏创建一个基本的怪物角色。首先建立一个数组以保存角色信息。

清单 3. 角色信息数组
$character = array(
    'name' => 'Fred The Zombie',
    'health' => '36',
    'gore' => '1',
    'clutch' => '5',
    'brawn' => '6',
    'sense' => '4',
    'flail' => '2',
    'chuck' => '3',
    'lurch' => '4',
);

需要生成文件名以保存这些信息。实际上不需要在传入时就使用输入,但是只可以使用人物姓名中的字母创建文件。

清单 4. 创建用于存储信息的文件
$filename = substr(preg_replace("/[^a-z0-9]/", "", 
      strtolower($character['name'])), 0, 20);
file_put_contents($filename, serialize($character));

注:在将该脚本提供给公共使用时,需要过滤用户输入以避免有人将错误的 JavaScript 代码或垃圾代码恶意插入文件中。这只是供您个人使用的示例。最后,需要用表单表示信息以便可以轻松更新。这只需使用输入字段中的默认值创建一张表单。

清单 5. 用表单显示信息
<input name='health' value='<?php echo $character['health'] ?>' />

使用此基本脚本(请参阅 代码归档)跟踪几个角色和 NPC。

NPC 生成器

在整合一个大型游戏时,拥有一个一次性的 NPC 池十分有用。如果需要大量暴徒或者士兵,这样做尤其有帮助。要整合 NPC 生成器,让我们构建一个包含构造角色规则的数组,然后使用随机姓名生成器及掷骰器一次性整合一批角色。

使用 Fred the Zombie 作为示例,您可以定义创建角色的规则。某些统计信息是固定的(生命力总是从 36 开始),某些统计信息是派生的(血=生命力/6),而某些统计信息是掷骰的结果(离合器是 1d6 — 一个六面骰子)。规则类似清单 6 中所示的内容。

清单 6. 定义角色创建的规则
$rules = array(
    'health' => '36',
    'gore' => 'health/6',
    'clutch' => '1d6',
    'brawn' => '1d6',
    'sense' => '1d6',
    'flail' => '1d6',
    'chuck' => '1d6',
    'lurch' => '1d6',
);

现在您可以编写少量代码来评估规则。

清单 7. 评估规则的代码
foreach ($rules as $stat=>$rule) {
    if (preg_match("/^[0-9]+$/", $rule)) {
        // This is only a number, and is therefore a static value
        $character[$stat] = $rule;
    } else if (preg_match("/^([0-9]+)d([0-9]+)/", $rule, $matches)) {
        // This is a die roll
        $val = 0;
        for ($n = 0;$n<$matches[1];$n++) {
            $val = $val + roll($matches[2]);
        }
        $character[$stat] = $val;
    } else if (preg_match("/^([a-z]+)\/([0-9]+)$/", $rule, $matches)) {
        // This is a derived value of some kind.
        $character[$stat] = $character[$matches[1]] / $matches[2];
    }
    echo $stat . ' : ' . $character[$stat] . "<br />\n";
}

在正确评估规则时,您可以引入使用随机姓名生成器生成的随机姓名并且一连几天制作 NPC。请查看 代码归档 中的脚本了解如何整合该任务。

机率计算器:掷骰

第 1 部分 中,您整合了一个简单的机率计算器以确定从给定的一副牌中抽到指定牌面或花色的机率。现在让我们构建一个掷骰机率计算器。对于本例,您将构建一个在掷两个六面骰子(2d6)时确定可能结果的计算器。如果使用的是基于 d6 的任何 RPG 系统,玩大富豪或双陆棋之类的棋盘类游戏,或者要尝试在诸如 Craps 之类的骰子游戏中计算出机率,这将十分有用。

首先,建立在掷两个骰子时确定可能结果的方法。可以考虑掷两个不同颜色的骰子(如一个红色骰子和一个蓝色骰子)。如果掷红色骰子的结果为 1,则有六个可能的结果,每个结果取决于蓝色骰子的一面。这意味着可能的掷骰结果总数为 6^2 即 36 个结果。这可能看似有些奇怪,因为您实际上可以得到的最高值为 12(红色六点加蓝色六点),但是如果您认识到可以通过两个不同的结果得到 11(红色六点加蓝色五点,或者红色五点加蓝色六点)应当更有意义。

让我们假定,在本例中,您需要同时获得总和(掷出 10)和骰面(掷出 6 和 4)结果,并且红色骰子的 6 点与蓝色骰子的 6 点没有差别。重要的数字为 S(骰子面数)和 N(骰子个数)— 假定有 N 个相同的骰子,最大结果数为 S^N — 可是这其中的一些结果可能相同(一个 6 和一个 4,以及一个 4 和一个 6)。

清单 8. 构建掷骰机率计算器
$s = 6;
$n = 2;
$results = array(array());
for ($i = 0; $i < $n; $i ++) {
    $newresults = array();
    foreach ($results as $result) {
        for ($x = 0; $x < $s; $x++) {
            $newresults[] = array_merge($result, array($x+1));
          
        }
    }
    $results = $newresults;
}

这可能看似有些沉闷。您可以适当地简化这段代码,但是我已经在这里完成了一些更明显的操作,因此您可以了解数组是如何处理的。在构建完最终数组后,您可以迭代数组并生成骰子数总和的列表及其各自的机率。这段代码包含在 代码归档 中。

简单的 Blackjack 扑克发牌器

第 1 部分 中,您构建了一个简单的扑克发牌器。该发牌器不是真正地在与您玩牌;它只是发出牌盒中的牌,从而允许您开始各种牌局。现在让我们构建一个真正与您玩牌的 Blackjack 发牌器。使用发牌器必须至少抽到 17 的简单内部规则构建此发牌器。

这部分代码将类似先前构建的简单的扑克发牌器。对于 Blackjack,您需要以迭代抽取及牌局评估的形式构建。您将需要一个把牌面值指定为数字描述的数组。如果更改构建牌组的方法,您可以在原始的 $faces 数组中完成此操作。

清单 9. 修改 Blackjack 的 $faces 数组
$faces = array('Two' => 2, ... ... ... 'King' => 10, 'Ace' => 11);

然后您需要一个基本函数来评估给定的牌局。该函数将查看手中的牌并返回手上的牌面总值。

清单 10. 评估给定牌局的函数
function evaluateHand($hand) {
    global $faces;
    $value = 0;
    foreach ($hand as $card) {
        if ($value > 11 && $card['face'] == 'Ace') {
            $value = $value + 1;  // An ace can be 11 or 1
        } else {
            $value = intval($value) + intval($faces[$card['face']]);
        }
    }
    return $value;
}

在评估手上牌局的函数就绪后,应当很容易确定您手上的牌是否超过 21 点,或者发牌器是否应当要牌。有关如何整合所有这些内容的详细信息,请参阅 代码归档

牌面计算器

在玩 Blackjack 或者诸如红心大战之类基于王牌的游戏时,知道如何算牌会十分有用。在玩 Blackjack 时,至少需要知道已经看到多少张 A 和花牌。对于红心大战,您可能需要知道游戏中还有多少王牌。您将构建一个 Blackjack 基本计算器。您可以采用相同原理构建王牌游戏的牌面计算器。

您将构建以略微不同的方式进行计算的牌组。您不必知道所有牌 — 只关注自己感兴趣的牌。对于单牌组牌盒,则类似如下所示:

清单 11. 构建单牌组牌盒的牌面计算器
$deck = array (
    'faces' => 16, // 10,J,Q,K * 4 suits
    'aces' => 4, // One per suit
    'other' => 32, // 52 - (16 + 4)
);

接下来,构建一张带有三个按钮的表单 — 一个用于花牌,一个用于 A,而另一个用于其他牌。通过在每次看到特定类型的牌时单击相应按钮,您可以调整留在牌组中的牌。

清单 12. 带有三个按钮的简单表单
if ($_POST['submit'] == 'faces') {
    $deck['faces']--;
}

然后通过查看修改后的牌组,可以计算抽到花牌、A 或其他牌的机率。

清单 13. 计算抽到花牌、A 或其他牌的机率
echo "Odds of pulling a face: " . $deck['faces'] . " out of " 
. $deck['faces'] + $deck['aces'] + $deck['other'];

使用这个基本牌面计算器作为起点,您可以构建一些更健壮的程序。

Bingo 引擎

基于我们在本文中已经完成的内容,您可以轻松地整合一个 Bingo 引擎以生成牌并从池中抽出编号。

Bingo 编号范围从 1 到 75,以 15 个编号为一组(1-15 分配给 B,16-30 分配给 I 等等)。按照构建并洗一副虚拟牌的相同方法,您可以构建并洗一套虚拟的 Bingo 球。但是您无需以相同方法跟踪牌面和花色。首先,构建基本编号集。

清单 14. 构建 Bingo 的基本编号集
for ($i = 1; $i < 16; $i++) {
    $numbers['B'][] = $i;
    $numbers['I'][] = $i+15;
    $numbers['N'][] = $i+30;
    $numbers['G'][] = $i+45;
    $numbers['O'][] = $i+60;
}

接下来,您可以置换每个子数组并且同时制作牌(不要忘记:中间的部分通常是一张任意的牌)。完成后,创建一个可用作牌组的主数组。

清单 15. 创建主数组
$letters = array ('B','I','N','G','O');
foreach ($letters as $letter) {
    shuffle($numbers[$letter]);
    $chunks = array_chunk($numbers[$letter], 5);
    $cards[$i][$letter] = $chunks[0];
    if ($letter == 'N') {
        $cards[$i][$letter][2] = '  '; // Free Space
    }
    shuffle($balls);
}

现在已经全部完成!分发牌,并开始从主集合中抽取虚拟 Bingo 球,就像先前抽牌所做的那样。在 代码归档 中可以找到此脚本的完整运行版本。

Jumble 助手

Jumble 是在大多数报纸中可以找到的字谜,您有一些已经完全混合在一起的单词。您必须从废话中提取出实际单词来,然后使用某些字母来创造新短语。从在 第 1 部分 中创建的纵横字谜助手开始,让我们整合一个快速脚本以帮助您构建 Jumble。

首先,打开单词列表。需要遍历该列表,将每个单词拆分为经过排序的字母数组,然后再次合并数组。例如,这样做将把单词 “COLOR” 改为字符串 “CLOOR”;该字符串表示单词 “COLOR” 中的所有字母,但是按照可预测的(而且更重要的是相配的)顺序。该字符串随后将被用作匹配单词的数组的关键字。它必须为数组,因为 “sword” 和 “words” 拥有完全相同的字母,但是顺序不同。最后,它将类似如下所示的内容:

清单 16. Jumble 的脚本
foreach ($words as $word) {
    $arr = str_split($word);
    sort($arr);
    $key = implode('', $arr);
    if (isset($lookup[$key])) {
        array_push($lookup[$key], $word);
    } else {
        $lookup[$key] = array($word);
    }
}

完成到此为止的程序后,只需将 Jumble 像任何其他单词一样处理,查找相应关键字的值并输出所有结果。

清单 17. 完成 Jumble 脚本
$arr = str_split($_POST['jumble']);
sort($arr);
$search = implode('', $arr);

if (isset($lookup[$search])) {
    foreach ($lookup[$search] as $word) {
        echo $word;
    }
}

您可以在 代码归档 中看到如何将这些内容整合在一起。

置换密码

置换密码 是一种简单的加密技术,采用这项技术将把字母表中的每个字母替换为字母表中的其他字母。按照现代加密技术的标准,置换密码简直就是小儿科。但是简单的置换密码玩起来很有趣,尤其是在 RPG 环境中给某个人传递一条可以在玩游戏的过程中破解出来的编码信息。

首先从 第 1 部分 的 Hangman 生成器中偷一些代码 — 也就是,字母数组。制作另外一份数组副本,搅乱该副本,并构建一个联合数组,其中原来的字母将获得重组后的值。

清单 18. 修改字母数组
$letters = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p',
'q','r','s','t','u','v','w','x','y','z');
$code = $letters;
shuffle($code);
$key = array_combine($letters, $code);

接下来,获取 $_POST 中的输入,将消息拆分为字母,然后将每个字母的值输出到密钥中。

清单 19. 分隔并输出字母
if (!empty($_POST)) {

    $messageletters = str_split(strtolower($_POST['message']));
    $show = '';
    foreach ($messageletters as $letter) {
        $show .= @$key[$letter];
    }
}

$key[$letter] 之前使用 @ 符号将告诉 PHP 忽略在编码期间发生的所有错误。如果消息有标点符号、数字或空格,PHP 将报告 $key 数组中没有相应值。这没问题。结果得到的编码文本将去掉空格、数字和标点符号,从而使它(略微)更加难于解码。

单词搜索生成器

单词搜索可能是我们学到的最早的基于字母的字谜。使用已有技术,再包括一些纵横字谜助手中的逻辑,您就可以创建一个非常基本的单词搜索生成器。为了保持简单,您将创建一个大小为 10x10 个字母,里面藏有 10 个单词(垂直方向 5 个,水平方向 5 个)的单词搜索。

第一部分,在水平方向放入单词非常简单 — 构建一个数组的数组,每个数组中有 10 个元素,用句点填充。然后在所有其他数组中,放入各个单词的字母。要放入垂直方向的单词,需要同时访问所有数组,使用纵横字谜助手代码查找可能匹配的单词。最棘手的部分可能是清单 20 中将单词或者随机字母填入垂直行(其中单词短于整行)的部分。

清单 20. 在垂直行中填充单词或者随机字母
$wordletters = str_split($word);
for ($c = 0; $c < $height; $c++) {
    if ( $grid[$c][$i] == '.' && ($c < $x || empty($wordletters))) {
        $grid[$c][$i] = $letters[rand(0,25)];
    } else  {
        if ($grid[$c][$i] == '.') {
            $grid[$c][$i] = array_shift($wordletters);
        } else if ($c >= $x) {
            array_shift($wordletters);
        }
    }
}

代码归档 包含了许多注释,可以帮助说明整个过程。该代码还有很大的性能改进空间,但是这应当已经向您展示了如何动手处理问题的一般概念。尝试代码归档中的完整脚本以查看结果。

结束语

本文说明了如何获取在 “用 PHP 可以编写的 30 个游戏脚本” 系列的 第 1 部分 中编写的一些基本脚本,以及如何基于这些基本脚本进行构建以提供更健壮的脚本。在第 3 部分中,您将继续基于本文构建的成果进一步提供更健壮、更有用的工具。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Open source
ArticleID=362166
ArticleTitle=可以用 PHP 编写的 30 个游戏脚本,第 2 部分: 开发 10 个中级脚本
publish-date=01052009