Tag Archives: 游戏

怪物猎人2G – 大剑 – 重剑无锋,大巧不工

先例行忏悔: 很久没写blog, 很对不起pangwa同学的辛苦维护工作。

所以为了pangwa的辛勤劳动耕耘,为了消耗掉今天从下午1点半到4点半的时间(除去中间半个小时的无聊大会),为了小肥妞不会天天被轰龙同学虐得满地找牙,也为了我以后不会忘记这些猫车无数换来的血泪经验,我决定将写“技术blog”方针政策贯彻执行,华丽地推出怪物猎人2G大剑技术贴一篇…

首先,我是菜鸟,目前只通了除了F4以外的所有村任务,和集会所上下位的全部关键任务,还在G1挣扎。其次,如同地球人都知道pangwa和我踢实况不输6个算赢的事实一样,地球人也知道玩MHP不到200小时算菜鸟,我只玩了100小时…好了,以上全是废话。接下来…就是见证另一坨废话的时刻!

FAQ #1: 玩MHP2G爽在哪?!为什么说不到长城非好汉,不杀轰龙枉英雄?

A #1: 刚买了PSP的人总是五花八门的游戏down下来乱七八糟地玩一通,发现都是些换汤不换药,很耗时间的很黄很暴力的游戏 (好吧,我在扯淡…)。最后总会怀念每天玩上一点点,又健脑益智,又美容养颜,又有成就感的MHP2G……以上是假话,其实MHP2G是一个无聊耗时间且自虐的游戏,如果您有大量时间可以浪费,如果您想体验摔机的快感,如果您厌倦了无双的割草想被当做草割,那么MHP2G是您不二的选择。

FAQ #2: MHP2G是像Diablo一样的多职业杀怪升级刷装备的游戏吗?

A #2: 不是。MHP2G的人物没有职业只有男女性别。MHP2G人物的攻击方式由武器决定(太刀、大剑、长枪、弓箭、大锤、…)。MHP2G的人物没有级别,只有HR(Hunter Rank),HR决定了能接任务的等级,HR的增长是通过完成各个狩猎任务得到的,HR增长了对人物的攻击力防御力血量等没有任何作用。MHP2G的防具作用不大,武器作用相对较大,但是就算用了顶级的装备,还是可能被下位小BOSS轻易虐杀。所以,这是一个技术流的游戏。和Diablo不像。

FAQ #3: 上位、下位、G位、村长任务是什么意思?

A #3: MHP2G里有两个体系的任务,一个是在村出口处村长婆婆和旁边的穿大衣的猫那里接的村任务。一个是可以联机的在村入口旁的集会所里三个前台MM给的任务。村长婆婆给的任务难度较低,成为村下位任务。大衣猫给的任务难度较高称为村上位任务。集会所里面的三个MM给的分别是难度从低到高的集会所下位、上位、G位任务。同等级的任务,集会所任务的怪物会强于村任务 (血量有所增加),集会所任务决定猎人的HR等级。一开始猎人只能接下位的村和集会所任务,打掉每个等级的关键任务(并不是需要清掉所有任务)之后再打过紧急任务就能升到下一等级的任务。

FAQ #4: 我是新人,我该用什么武器?什么武器比较好上手?

A #4: 我k,看我标题啊!一般来说太刀、双刀比较容易让新人接受,因为速度和操作感和其他动作类的游戏相差不大。但是太刀双刀招式繁复(比如我们小肥妞同学就记不住太刀的连招)并且容易养成贪刀的坏毛病,且不能防御。大剑相对来说单次攻击力很高 – 不需要贪刀所以消耗斩味也很慢,招式简单 – 除开防御就三招,能够防御,且动作迟缓 – 动作迟缓才不容易紧张嘛…且不用很麻烦地为了打不同种类的怪物换各种属性的武器,一般来说一把无属性高攻的大剑能通杀所有怪物。另:MHP2G的招牌性感小MM就是穿麒麟套装拿麒麟召雷剑的…

0a1e1800e4247830738b650edl_w03

大剑招式

拔出剑之前:三角是拔刀,摇杆+三角(跑动中拔刀)是速度很快攻击很强的拔刀(纵)斩,摇杆+三角按住不放是直接拔刀蓄力(能蓄三段,威力大概是普通拔刀斩的三倍),释放是纵斩。三角+圆圈+R键三个键一起按住不放是紧急防御。

拔出剑之后:三角是速度很慢攻击很强地纵斩,圆圈是速度比较快范围很大攻击最弱的横斩,三角+圆圈是速度很慢范围一般、斩击高度最高距离最远、能斩到身后、攻击次强的上斩。按住R键是防御。

连招:大剑三招(纵、横、上)的任意两招都能无限连。纵、上和纵、横组合会使猎人前移,横、上组合猎人一直保持原地。所以一般大剑无限连指的都是横、上组合,代表使用时机是在大型怪物(比如沙龙王)的肚子底下…

大剑的所有招式(包括在蓄力过程中)在挥动时都无视风压。风压指的是怪物的一些行动(比如大怪鸟扇风飞起并往后降落)会伴随大风,这时猎人会做出挡风的动作并且不能行动直到怪物动作完成。所以使用大剑的猎人可以顶风作案 – 例如在雄火龙快降落之前蓄力,虽然火龙降落时会有风压,但是不会影响蓄力,然后在火龙降落到面前时强力一击。

龙风压是风压的威力加强版,凤翔龙、炎王龙之类的古龙类都有龙风压围绕着身体,在没有龙风压无效技能的情况下,大剑也不能抵挡龙风压。

大剑战法:

一击脱离是大剑的精髓。所谓“一击”,指的是平时收刀在怪物身边绕圈游走,在怪物出现破绽的时候,用速度非常快的拔刀斩攻击怪物。如果怪物出现大破绽,比如电龙吐电球,可以用拔刀蓄力斩。如果能斩击到怪物的弱点部位,配合到了上位、G位能出的拔刀术技能(拔刀斩必出会心一击,125%的伤害),具备强悍攻击力的大剑一击能造成很大伤害并且容易使怪物出现硬直(比如三段蓄力攻击轰龙的头)和倒地(比如攻击盾蟹的脚)。所谓“脱离”,指的是斩击之后按叉键配合摇杆向某个方向翻滚来取消斩击后举刀的硬直,并且逃离出怪物的攻击范围。有时候可能需要翻滚多次。注意斩后的翻滚方向只能有三个:一是向斩击方向(向前),一个是往左,一个是往右。

所以,一击脱离 = 游走寻找破绽 -> 按三角拔刀斩 -> 按叉翻滚躲避到安全区域 -> 收刀 -> 继续游走寻找破绽(循环)

除了一击脱离,在大型怪物身下无限连(横斩+上斩)也是一种比较常见的打法。写具体各个怪物的打法会提到。

在怪物身边游走时注意要时刻观察怪物的动向,高手(或者说BT…)会采用一种叫做C手的技巧,用左手拇指控制摇杆,用食指控制方向键转换视角,用中指按L。我们小菜鸟之流就老老实实都用拇指吧…

关于斩位:

“斩位”指的是武器的锋利程度。在进入战斗后的画面左上角可以看到一把泛着光的刀的形状,这就是斩位条,它的颜色就代表了斩位。从低到高一共有红、橙、黄、绿、蓝、白、紫几种斩位。斩位主要影响两方面: 一是有的时候砍怪会弹刀(比如拿大骨剑去砍大怪鸟),说明斩位太低;二是斩位越高,攻击加成越高 – 比如同样攻击力为600的刀,斩位是绿斩的实际攻击力大于斩位是黄斩的。

在砍怪过程中,武器会逐渐变钝,称之为“斩味消耗”,比如绿斩的刀砍一会儿就会变成黄斩。用砥石可以恢复斩位等级。斩味消耗速度取决于攻击频率,所以单次攻击力很高而攻击频率很低的大剑消耗斩味很慢。

大剑选择:

下位可以先走巨剑系的升级路线,做出巨剑系的爆裂刃改,这把剑只需要挖挖矿石就能出来,绿斩700多的攻击力,相当不错。到了村3星杀掉盾蟹王之后可以做出赤纹刀放着,然后到了村4星能杀镰蟹王之后可以升级两次做出蓝色钳刃(注意要破钳才能拿到必需的制作材料镰蟹之铗),800多攻击的绿斩刀,用到上位没有问题了。

到了上位村8星能杀上位镰蟹王之后杀掉(可怜的镰蟹…)把蓝钳刃升级做出苍剪刀,1008攻击自带蓝斩的好剑啊。在上位运气足够好的话 – 能刷出火龙的红玉和一角龙的心脏的话 – 可以做出上一作(MHP2,没有G级任务)最强大剑齐格蒙大剑,1056攻击自带蓝斩会心20%且防御+8。还有一个选择就是杀掉迅龙可以做出的迅龙大剑。虽然只有900多攻击,但是自带白斩和50%会心还是很强大的。直接制作迅龙大剑需要迅龙延髓比较难拿,所以可以先做出轰龙大剑(需要轰龙头壳,不过上位不少)然后再升级。在上位一般都会有“匠(斩位+1)”这个技能,所以迅龙大剑能出紫斩,紫斩(150%物理伤害修正)比白斩(135%物理伤害修正)的攻击力要高出相当不少。

到了G1用上位大剑打着可能比在上位村7星用下位蓝钳刃打着要更吃力一些。有杀怪的决心可以撑到G2继续杀镰蟹王(真可怜…)做出巨剪,接近1300的攻击力自带白斩防御+40。没有决心的话(比如我…)可以挖矿做矿石系的天罚,同样接近1300的攻击力自带白斩。天罚可以从灵鹤大剑升级上来也可以直接做抉择之刃升一级。G3过后可以做拔刀系大剑猎人人手一把的终极大剑:角王剑了。配合拔刀术技能可以无视角王剑的15%负会心。

防具选择:

下位穿免费的雪山套可以打到2星。不过因为技术还不够纯熟可能需要去找猫奸商买套战斗装或者猎人装(或者随便做点什么蓝速龙装,大怪鸟装之类的)来撑到3星,3星杀掉盾蟹后可以做一套盾蟹装提升防御力。杀掉小轰同学后可以做出下位校服轰龙装,加几个研磨珠可以出快速磨刀,配合自带的速食和千里眼,毫无疑问的毒品套装…(用金手指显地图者无视…)不嫌丑的可以做黑狼鸟套,配伪装头可以出高耳+利刃+见切。女号强烈推荐美型第一之麒麟装。啧啧…

到村上位后靠下位装打到8星,上位镰蟹王再献大礼一份 – 虐杀之,做出价廉物美的镰蟹S装,装备自带9个洞,有匠(斩位+1)+攻击力上升(小)+防御-20的技能。可以很奢侈浪费地配合一个1洞武器打上10个拔刀珠出拔刀术,也可以打攻击珠研磨珠配出攻击力上升(大)和快速磨刀。看不惯防御-20的可以打防御珠去掉这个负面技能,不过我们说,怪物猎人的世界里,防御力是浮云…花点铠玉强化一下防具吧,虽然是浮云,毕竟要靠这套防具来撑过G级的初级任务…

G1没有什么比较好的防具,撑到G2, G级镰蟹王继续出来打酱油(真可怜…)- 照例先杀后挖,做出镰蟹ZX混装,能轻松配出拔刀术+匠+业物。好了,靠这身装备混过G级接下来的任务,杀掉金毛后做出人手一套的大剑校服真金狮套,拔刀术+集中+匠,不是一般的强悍。

且听下回分解:

果然我又下笔千言离题万里了…写了半天还没开始到各个怪物的打法经验,看来只能下次了…

Bresenham算法

上回说到, 在看一本书《Windows游戏编程大师技巧》 (Tricks of Windows Game Programming Gurus). 这次继续书里的内容: 直线光栅化的Bresenham算法. 书上讲的比较含糊, 没有讲算法的推导过程, 更没讲算法是怎么想出来的. 所以我们只好自己动手, 丰衣足食…

直线光栅化

直线光栅化是指用像素点来模拟直线. 比如下图中用蓝色的像素点来模拟红色的直线. 图中坐标系是显示器上的坐标系: x轴向右, y轴向下.

bresenham

设deltaX = endX – startX, deltaY = endY – startY. 那么斜率为k = deltaY / deltaX. 我们先考虑简单的情况: 当 0 < k < 1即直线更贴近x轴. 在这种情况下deltaY < deltaX, 所以在光栅化的过程中, 在y轴上描的点比在x轴上描点少. 那么就有一个很直观的光栅化算法:

line_bresenham(startX, startY, endX, endY)
{
    deltaX = endX - startX;
    deltaY = endY - startY;
    k = deltaY / deltaX;
 
    <span style="color: #0000ff">for</span> (x = startX, y = startY; x &lt;= endX; ++x)
    {
        <span style="color: #0000ff">if</span> (满足一定条件)
        {
            ++y;
        }
        drawPixel(x, y);
    }
}

基于斜率 / 距离的两个简单直线光栅化算法

好了,貌似很简单, 就剩一个问题: “满足一定条件”是什么? 可以用斜率判断, 也可以用上图中直线与光栅线交点 (红点) 光栅点 (蓝点) 的距离来判断. 继续用伪代码说话:

<span style="color: #008000">// 算法1: 用斜率判断</span>
<span style="color: #0000ff">void</span> line_bresenham_k(startX, startY, endX, endY)
{
    deltaX = endX - startX;
    deltaY = endY - startY;
    k = deltaY / deltaX;
 
    <span style="color: #0000ff">for</span> (x = startX, y = startY; x &lt;= endX; ++x)
    {
        <span style="color: #0000ff">if</span> (x - startX != 0)
        {
            <span style="color: #008000">// 计算当前斜率</span>
            currentK = (y - startY) / (x - startX);
 
            <span style="color: #008000">// 如果当前斜率 &lt; k, 则增加y坐标</span>
            <span style="color: #0000ff">if</span> (currentK &lt; k)
            {
                ++y
            }
        }
        drawPixel(x, y);
    }
}
 
<span style="color: #008000">// 算法2: 用距离判断. 计算直线与光栅线交点y坐标我们需要用到</span>
<span style="color: #008000">// 直线方程 y = k (x - startX) + startY</span>
line_bresenham_dist(startX, startY, endX, endY)
{
    deltaX = endX - startX;
    deltaY = endY - startY;
    k = deltaY / deltaX;
 
    <span style="color: #0000ff">for</span> (x = startX, y = startY; x &lt;= endX; ++x)
    {
        <span style="color: #008000">// 计算直线与光栅线交点的y坐标, 以及与光栅点的距离</span>
        ptY = k * (x - startX) + startY;
        dist = ptY - y;
 
        <span style="color: #008000">// 如果距离 &gt; 0.5或者 &lt; -0.5, 说明我们需要增加y以</span>
        <span style="color: #008000">// 将距离的绝对值控制在0.5之类</span>
        <span style="color: #0000ff">if</span> (dist &gt; 0.5 || dist &lt; -0.5)
        {
            ++y;
        }
        drawPixel(x, y);
    }
}

消灭浮点数!

以上都是很直观的算法, 下面不直观的来了 – 上面的算法都需要在循环体内执行乘法, 准确的说, 是进行浮点数的乘法. 我们怎么能减少这些浮点数的乘法开销呢? 以基于距离的算法2为例: 首先, k是一个浮点数, 0.5也是浮点数. 我们可以通过将这些表达式都乘以2 * deltaX (整数) 来解决浮点数的问题. 伪代码:

<span style="color: #008000">// 算法3: 在算法2的基础上消灭浮点数!</span>
line_bresenham_dist(startX, startY, endX, endY)
{
    deltaX = endX - startX;
    deltaY = endY - startY;
 
    <span style="color: #0000ff">for</span> (x = startX, y = startY; x &lt;= endX; ++x)
    {
        <span style="color: #008000">// 计算直线与光栅线交点的y坐标, 以及与光栅点的距离</span>
        ptY1 = deltaY * (x - startX) + startY * deltaX;
        dist1 = ptY1 - y * deltaX;
        dist1 = dist1 &lt;&lt; 1; <span style="color: #008000">// dist1 = dist1 * 2</span>
 
        <span style="color: #008000">// 如果距离 &gt; 0.5或者 &lt; -0.5, 说明我们需要增加y以</span>
        <span style="color: #008000">// 将距离的绝对值控制在0.5之类</span>
        <span style="color: #0000ff">if</span> (dist1 &gt; deltaX || dist &lt; -deltaX)
        {
            ++y;
        }
        drawPixel(x, y);
    }
}

消灭乘法!

圆满解决浮点数运算问题! 不过…乘法运算还在. 消灭乘法问题的办法比较不直观, 让我们想一想: 还有什么办法能简化运算. 直线方程已经不能再简化, 所以唯一的突破口就是能不能利用递推 / 用上一次循环的计算结果推导下一次循环的计算结果.

首先我们来看看在算法2的基础上 (因为算法2计算红点蓝点之间的距离, 比较直观), 怎么通过第n – 1次循环计算出的dist值 (设为d1) 来推导出第n次循环的dist值 (设为d2). 先回顾一下: dist = 直线与光栅线交点的y坐标 – 相应光栅点的y坐标. 我们从几何上直观地考虑: 在第n次循环中, 我们先根据上一次循环所计算出来的d1, 暂时令d2 = d1 + k, 因为我们要保证-0.5 < d2 < 0.5, 而d1 + k满足d1 + k > –0.5, 所以我们只需要考虑当d1 + k > 0.5时, 我们需要将光栅点y坐标增加1, 并且将d2减去1. 显然, 设y1是第n – 1次循环中光栅点的y坐标, y2是第n次循环中光栅点的y坐标. 我们有
1) d2 = d1 + k –  (y2 – y1)
2) 当d1 + k > 0.5时y2 = y1 + 1, 否则y2 = y1
我们已经能根据上面的两个关系式写出算法, 不过为了消除乘法和浮点数, 我们将这两个关系式两端同时乘以2 * deltaX, 并且设e = 2 * deltaX * d, 则我们有
3) e2 =  e1 + 2 * deltaY – 2 * deltaX * (y2 – y1)
4) 当e1 + 2 * deltaY > deltaX时y2 = y1 + 1, 否则y2 = y1
终于, 没有了乘法 (2 * deltaY在循环体外计算且被简化为左移一位的运算), 没有了浮点数, 根据关系式3) 和 4), 写出算法:

<span style="color: #008000">// 算法4: 在算法2, 3的基础上利用递推消灭乘法和浮点数!</span>
line_bresenham(startX, startY, endX, endY)
{
    deltaX = endX - startX;
    deltaY = endY - startY;
    e = 0;
    deltaX2 = deltaX &lt;&lt; 1;
    deltaY2 = deltaY &lt;&lt; 1;
 
    drawPixel(startX, startY);
 
    <span style="color: #0000ff">for</span> (x = startX + 1, y = startY; x &lt;= endX; ++x)
    {
        <span style="color: #008000">// 关系式3) e2 =  e1 + 2 * deltaY – 2 * deltaX * (y2 – y1)</span>
        <span style="color: #008000">// 关系式4) 当e2 + 2 * deltaY &gt; deltaX时y2 = y1 + 1, 否则y2 = y1</span>
        e += deltaY2;
        <span style="color: #0000ff">if</span> (e &gt; deltaX)
        {
            e -= deltaX2;
            ++y;
        }
        drawPixel(x, y);
    }
}

消灭浮点数! 代数推导

上面递推关系的推导过程是从图形上”直观”地分析得来的, 但是不严密. 我们能不能形式化地证明关系式1), 2), 3), 4)呢? 因为关系式3), 4)和1), 2)能互相推导, 我们只证明3), 4)如下:

在算法3的基础上设第n – 1次循环计算出的dist1值为e1, 对应的y值为y1, 第n次循环计算出的dist1值为e2, 对应的y值为y2. 根据算法3,
dist1 = 2 * deltaY * (x – startX) + 2 * startY * deltaX – 2 * y * deltaX, 则
e2 – e1
= 2 * deltaY * (x – startX) + 2 * startY * deltaX – 2 * y2 * deltaX – [2 * deltaY * (x – 1 – startX) + 2 * startY * deltaX – 2 * y1 * deltaX ]
=  – 2 * y2 * deltaX + 2 * deltaY + 2 * y1 * deltaX
= 2 * deltaY – 2 * deltaX * (y2 – y1)
所以e2 = e1 + deltaY – deltaX * (y2 – y1). 所以我们有关系式
1) e2 = e1 + 2 * deltaY – 2 * deltaX * (y2 – y1)
2) –deltaX <  e1 < deltaX
3) –deltaX < e2 < deltaX
4)  y2 – y1 = 0 或者 1 
我们根据e1 + 2 * deltaY的取值范围进行讨论. 首先, 因为不等式6), 我们有
2 * deltaY – deltaX < e1 + 2 * deltaY < 2 * deltaY + deltaX

情况1: 如果2 * deltaY – deltaX < e1 + 2 * deltaY < deltaX, 则
2 * deltaY – deltaX – 2 * deltaX * (y2 – y1) < e2 < deltaX– 2 * deltaX * (y2 – y1) 
证: 若y2 – y1 = 1, 则 2 * deltaY – deltaX – 2 * deltaX < e2 < deltaX – 2 * deltaX = -deltaX, 所以y2 – y1 = 1不成立. 即情况1中y2 = y1.

情况2:  如果 deltaX < e1 + 2 * deltaY < 2 * deltaY + deltaX, 则
deltaX – 2 * deltaX * (y2 – y1) < e2 < 2 * deltaY + deltaX – 2 * deltaX * (y2 – y1)
反证: 若y2 – y1 = 0, 则 deltaX < e2 < 2 * deltaY + deltaX 所以y2 – y1 = 0不成立. 即情况2中y2 = y1 + 1.

打了这么多字, 累…以上就是当0 < k < 1的情况, 剩余几种情况 (k > 1, –1 < k < 0, k < –1. 不要挑剔我不用”>=”这种符号…) 都可以通过简单的x, y交换以及正负交换来搞定.