内容


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

创建 10 个高级脚本

Comments

系列内容:

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

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

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

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

在本文中,我们将为角色扮演游戏构建一个清单管理系统及提示脚本,同时处理 PHP 脚本的交互操作。我们还将构建基于图像的 ID 卡片生成器并且尝试用 PHP 执行图像处理。我们将创建一个扑克牌求值程序、老虎机生成器及博彩银行,允许您实践一些更棘手的 PHP 逻辑。我们将通过构建一个复杂的变位词生成器、解码器等,进一步处理文字游戏脚本的复杂逻辑。

这些脚本比 第 1 部分第 2 部分 中的脚本难一些,因为它们将完成更多操作。本文的 代码归档 包含所讨论的每个脚本的完整源代码,并且可以访问 http://chaoticneutral.net/3d10 查看脚本实际运行情况。

第 1 部分第 2 部分 中,我们非常快速地介绍了这些脚本。如果尚未阅读过第 1 部分和第 2 部分,您应当阅读前两个部分,然后再开始阅读本文。

基本武器清单管理系统

在整合了管理角色统计信息的脚本后,让我们进一步丰富脚本并且添加一些基本武器清单管理功能。使用此脚本,可以跟踪耗费的弹药、燃烧的火把及找到的装备(参见清单 1)。这里并没有尝试展示您可能找到的所有装备,我们将只创建两个输入字段 — 一个用于装备名称,另一个用于装备计数:<input name='newitem' /> <input name='newcount' />。在提交脚本后,需要将新装备添加到武器清单数组中,并且连同角色表一起保存数组。然后遍历武器清单数组并创建新输入字段以保存装备。

清单 1. 添加一些基本清单管理
foreach ($character['inventory'] as $name => $count) {
    echo $name . " : <input name='inventory[" . $name . "]' value='" .
$count . "' /><br />";
}

这些输入名称可能看上去有些怪异,但是通过以这种样式构造名称,PHP 将自动创建 “inventory” 数组,并且填充了名称-值对。参阅脚本归档以查看如何整合。

简单提示脚本

在玩 RPG 时记录一些提示十分有帮助。如果忘记了将重要提示记录到了哪些位置,提示就没有那么有帮助了。让我们整合一个帮助存储和找到游戏提示的脚本。

记录提示只需要两个表单字段:<input name='title' /> <br /> <textarea name='body'> <br />

与角色表一样,提示将被添加到数组中,经过序列化并存储到文本文件中。在保存提示时,您将分析单词。单词计数也存储在同一个文本文件中。为了简单起见,替换不是字母或数字的内容并且将整个提示小写(只用于分析):$analyze = preg_replace('/[^0-9a-z\s]/', strtolower($_POST['body']));。然后用空白分隔提示并把提示 ID 放入每个单词的单词计数数组。

清单 2. 帮助轻松存储并查找游戏提示的脚本
$noteid = count($masternotes);
$masternotes[] = array('title' => htmlentities($_POST['title']), 
        'body' => htmlentities($_POST['body']));
$words = preg_split('/\s/', $note['body'] . ' ' . $note['title']);
foreach ($words as $word) {
    $word = preg_replace('/[^0-9a-z]/', '', strtolower($word));
    if (!in_array($word, $ignore_these_words)) {
        if (isset($counts[$word])) {
                $counts[$word][$noteid] = $noteid;
        } else {
            $counts[$word] = array($noteid => $noteid);
        }
    }
}

通过尝试使用测试脚本或者在 代码归档 中查找,您可以查看如何按关键字检索单独提示或者多个提示。

ID 卡片生成器

如果要玩现代游戏,为您的角色使用一张 ID 卡片可以给您的朋友留下深刻印象并且给游戏添加一些现实感。您将整合一些 PHP 图像处理代码,这些代码将允许您创建适合贴膜(lamination)的 ID 卡片图像。首先,需要一张基本图像用作空白 ID。我们将使用这张未经过加工的空白 ID 作为示例。

图 1. 空白示例 ID
空白示例 ID
空白示例 ID

需要一张基本表单来收集 ID 卡的字段(名字、授权、家庭住址、出生日期、头发颜色和眼睛颜色)。然后需要把这些值作为字符串传递到图像中。在确定开始文本和停止文本的正确位置时,这其中的一些值将进行检验并纠正错误的值:imagettftext ( $img , 40 , 0 , 600 , 200 , 0 , "tarzeau_-_OCR-A", $_POST['name'] );。如果一切都正确完成,您应当能够填充表单并且得到图 2 中所示的内容。

图 2. 填写完的示例 ID
填写完的示例 ID
填写完的示例 ID

指定的字体附带在代码归档中,并且源图像为 300DPI。在某些系统中,使用字体和 ID 卡可能十分棘手,但是脚本归档中的源代码将能够正确工作。

基于图像块(tile)的地图生成器

早期计算机游戏使用了图像块创建游戏地图。如今在某些情况下,仍然使用这种基本的基于图像块的方法。让我们整合一个生成基本地图的脚本。这张地图不会非常大,但是我们可以使用相同的原理创建含有不同详细信息的更大型地图。首先,创建一些不同的地形类型并且集合一些极为初步的地图图像块以表示每个地形类型。

图 3. 基本地形类型
基本地形类型
基本地形类型

现在有了基本地形类型,您可以想象得到接下来需要做什么:为每一行创建一个地图图像块数组。但是,只随机获取地形类型并放入数组中,我们将得到一个不怎么好的结果。

让我们制作一个大小为 20 个图像块的地图。在获取地形类型时,考虑周围的图像块。查看四个最近的已知图像块:当前行中的最后一个图像块以及当前图像块上面紧挨的三个图像块。我们可以设置复杂规则来控制如何生成地形,但是现在,从简单的内容开始:向数组中添加四个最相邻的图像块的地形类型,然后再随机获取。添加先前的地形类型的次数越多,获得相似区域的可能性就越多。结果是类似清单 3 中所示的内容。

清单 3. 地图图像块数组
$map = array();
$terrain = array ('plains', 'forest', 'swamp', 'hills', 'mountain', 'water');
for ($row = 0; $row < 20; $row++) {
    $map[] = array();
    for ($column = 0; $column < 20; $column++) {
        $pool = $terrain;
        if (isset($map[$row-1])) {
            if (isset($map[$row-1][$column-1])) {
                $pool[] = $map[$row-1][$column-1];
                $pool[] = $map[$row-1][$column-1];
            }
            $pool[] = $map[$row-1][$column];
            $pool[] = $map[$row-1][$column];
            if (isset($map[$row-1][$column+1])) {
                $pool[] = $map[$row-1][$column+1];
                  $pool[] = $map[$row-1][$column+1];
            }
        }
        if (isset($map[$row][$column-1])) {
                $pool[] = $map[$row][$column-1];
                $pool[] = $map[$row][$column-1];
        }
        shuffle($pool);
        $map[$row][$column] = $pool[0];
    }
}

在拥有地图数组后,我们将遍历每行和每列,包括每个地形类型的图像。尝试执行测试脚本以查看此脚本将生成哪类地图。此脚本是没有任何限制的。我们可以通过将地图类型多次放入脚本中,为各种地形类型提供不同的权重,并且创建地形类型可以实现的更复杂规则。请一定要大胆实践。

扑克牌求值程序

我们已经编写了一个扑克发牌器和 Blackjack 发牌器。让我们进一步编写扑克牌求值程序。该程序更加复杂,并且处理如何排序同类牌可能需要花些时间。但是让脚本查看五张牌并且告诉您点数是否一样、花色是否一样等十分简单。

在查看手中的牌时,有几种简单的规则可以应用:

  1. 如果当前牌的花色不同于上一张牌的花色,则没有同花的五张牌。
  2. 如果任意牌的牌面与任何先前牌的牌面相匹配,则没有点数一样的五张牌。
  3. 如果没有牌匹配,并且 $highcard 减去 $lowcard 刚好等于五,则有点数一样的五张牌。

准备好这些简单规则后,应用一个简单检查来计算出匹配:

  1. 如果有五张不同的牌面,则没有匹配。
  2. 如果有四张不同的牌面,则有一对。
  3. 如果有三张不同的牌面,则有两对或三张相同的牌。
  4. 如果有两张不同的牌面,则有满堂红或四张相同的牌。
  5. 如果只有一张牌面,则已经犯规。

通过查看归档中的样例脚本,查看这些规则如何应用。

老虎机

现代老虎机是复杂的机器,拥有视频屏幕、多条赔付线(payline)、更多按钮并且只需拉动更少拉杆。对于本例,我们将模拟一条赔付线的简单三轮旋转老虎机。让我们首先识别转轮上的各个面。

清单 4. 识别转轮上的各个面
$faces = array ('Cherry', 'Bar', 'Double Bar', 'Triple Bar', 'Diamond', 'Seven');

接下来,确定赢奖结果。赢奖结果可以有许多变形,但是为了举例,让我们假定赢奖结果是在一条赔付线上有三个匹配的结果。需要设置赢奖结果与奖金的一些关联性。您可能会看到类似清单 5 中所示的内容。

清单 5. 赢奖结果和结算奖金
$payouts = array (
    'Bar|Bar|Bar' => '5',
    'Double Bar|Double Bar|Double Bar' => '10',
    'Triple Bar|Triple Bar|Triple Bar' => '15',
    'Cherry|Cherry|Cherry' => '20',
    'Seven|Seven|Seven' => '70',
    'Diamond|Diamond|Diamond' => '100',
);

在转动转轮时,第一个轮和第三个轮朝一个方向转动,而第二个转轮朝另一个方向转动。记住这个原理后,让我们设置转轮。

清单 6. 设置转轮
$wheel1 = array();
foreach ($faces as $face) {
    $wheel1[] = $face;
}
$wheel2 = array_reverse($wheel1);
$wheel3 = $wheel1;

在代码中,我们反转了数组来设置 $wheel2。我们希望跟踪各个游戏中的转轮位置,但是其余则非常简单。每个转轮至少必须转一圈,不超过 10 圈。使用模数来模拟滚动转轮:$result1 = $wheel1[rand(count($wheel1), 10*count($wheel1)) % count($wheel1)];。最后,在 $payouts 中查找结果。

清单 7. 在 $payouts 中查找结果
if (isset($payouts[$result1.'|'.$result2.'|'.$result3])) {
    // give the payout
}

请注意:这是非常初级的。实际的老虎机要复杂得多,并且结算奖金十分棘手。这只是一次练习。使用脚本归档中的代码并查看是否可以把奖金调整为更合理的金额。

基诺

基诺是 bingo 与乐透的混合体。从 1 到 80 随机选择二十个数字,玩家把赌注下到将抽出的数字上(通过取出最多 15 个数字)。基诺奖金迥然不同,但是设置一个脚本来模拟基诺非常简单,尤其是在我们已经在本系列中执行过类似内容之后。对于初学者,您需要包含从 1 到 80 的数字数组。

清单 8. 包含 1 到 80 的数字数组
$balls = array();
for ($i = 1; $i <= 80; $i++) {
    $balls[] = $i;
}

shuffle($balls);

在单个文本字段中输入您的猜测结果(用逗号分隔)。然后只需从 $balls 数组中选取 20 个数字,并且针对抽取的号码检查猜测结果。这是可以轻松完成的,并且可以在 代码归档 中看到。可以通过在基诺样式板中显示选择结果来改进脚本。请在熟悉脚本后再尝试这样做。

密码助手

密码术是在大多数报纸中流行的游戏,目标是将一个使用置换计算程序编码的给定短语解码,类似于在 第 2 部分 中编写的置换计算程序。让我们编写一些程序以使用基本频率分析将编码信息解码。您可以使用同一种方法来帮助解决密码问题。

频率分析只是 “对字母进行计数” 的另一种有趣表达。获取一个未编码的文本样例,计算文本中出现特定字母的次数,然后按频率最高到最低的顺序给字母排序。然后对编码文本执行相同操作。完成时,把两张字母列表放在一起,然后获得对解密密钥的合理猜测。这种方法适用于大型文本块,但是在处理较短的短语时仍然可以使用这种方法。首先创建两个文本框:一个用于未编码的文本,另一个用于密码。计算未编码文本中的字母并将数组结果排序。

清单 9. 计算未编码文本中的字母并将数组结果排序
$unencoded = str_split(preg_replace('/[^a-z]/', '', strtolower($_POST['unencoded'])));
foreach ($unencoded as $letter) {
    $lettercount[$letter]++;
}

arsort($lettercount);

然后,对编码文本执行相同操作。在将数组按字母频率排序后,创建字母图并使用它们尝试将文本解码。如果您特别幸运,文本可以一次解码成功,但是不要指望它。结果可能是部分解码的文本 — 这是解出谜题的良好起点。查阅脚本样例了解更多详细信息。此脚本有很多改进空间。

策略游戏(Mastermind)

策略游戏是一款桌面游戏(基于较旧的游戏),玩家必须猜测代码生成器设置的圆点的顺序和颜色。在此版本中,脚本将是代码生成器,然后您将做出一系列猜测。脚本将告诉您有多少次猜中了正确的颜色和错误的位置;以及有多少次猜测是完全正确的。策划人将使用六种不同颜色的圆点,并且可以重复使用颜色。首先建立如下所示的代码。

清单 10. 六种不同颜色圆点的数组
$pegs = array ('R','O','Y','G','B','V');
$code = array();
for ($i = 0; $i < 4; $i++) {
    $code[] = $pegs[rand(0,5)];
}

接下来,我们需要分析猜测结果(参见清单 11)。猜测结果将被输入到一个文本字段中,例如 OOGBVR。

清单 11. 分析猜测结果
$guess = str_split($_POST['guess']);
if ($guess == $code) {
    // the code has been guessed.
} else {
    for ($i = 0;$i < 4;$i++) {
        if ($guess[$i] == $code[$i]) {
            // A correct guess
        } else {
            // Keep track of the guessed colors, for output later
        }
    }
}

查看如何将它全部整合到样例脚本中。这是一款乐趣横生的游戏,可以通过多种方式进行修改。

单词链

您可能曾经玩过这样一款游戏,得到一个单词,然后只更改一个字母就可以得到另一个单词。通常,这款游戏的玩法是将一个单词更改为另一个单词,就像使用以下单词链将单词 BIKE 更改为单词 FATE:BIKE、LIKE、LAKE、LATE、FATE。让我们使用为纵横字谜助手创建的一些代码,创建从一个单词生成单词链的脚本(参见清单 12)。给定一个单词,如 BIKE,我们可以创建缺少字母的四个单词(.IKE、B.KE、BI.E 和 BIK.),可以将这些单词传递给纵横字谜助手以查找可能的匹配。

清单 12. 通过一个单词生成单词链
$letters = str_split($link);
foreach ($letters as $key => $letter) {
    foreach ($words as $word) {
        if ($key == 0) {
            $guess =  '.' . substr($link, 1);
        } else if ($key+1 == count($letters)) {
            $guess =  substr($link, 0, $key) . '.';
        } else {
            $guess =  substr($link, 0, $key) . '.' . substr($link, $key+1);
        }
        if (preg_match("/^" . $guess . "$/",$word)) {
            $matches[] = $word;
        }
    }
}

在找到匹配后,我们将对匹配执行相同操作(确保不重复单词)。重复执行这种逻辑,直到找到所有匹配。我们甚至可以针对每个可能的匹配执行这种方法。这样做将是十分有用的练习,并且对失控递归非常有用。在熟悉样例脚本后,您应当做这样一次尝试。

结束语

我希望 “可以用 PHP 编写的 30 个游戏脚本” 这一系列文章可以帮助您开始用 PHP 创建有趣的游戏脚本。无论您多么喜欢玩游戏,都可以使用本系列中提供的简单实用的程序丰富游戏经验。PHP 是执行这些任务的最佳选择,而且如果您在本文之后继续钻研,则在获得乐趣的同时一定会提高 PHP 编程技巧。


下载资源


相关主题


评论

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Open source
ArticleID=363057
ArticleTitle=用 PHP 可以编写的 30 个游戏脚本,第 3 部分: 创建 10 个高级脚本
publish-date=01122009