游戏评测6——Terraria

挖矿刷怪打BOSS,走向人生巅峰!!!
image


这里首先需要说明一点,如果强迫症患者的话请谨慎进行这款游戏,因为这玩意玩起来根本停不下来哈哈哈哈哈(不要问我为什么 = =)。

Terraria(泰拉瑞亚)于2011年5月16日在PC端首次与玩家们见面,这样的一款沙盒游戏在经历了几次大的变动后逐渐成为了现在玩家所看到的样子。2014年5月16日更新了最新版本1.2.4.1。值得一提的是虽然之前制作组已经宣布了不再更新游戏但却嗯……(良心)这一版本的更新修复了诸多玩家在游戏中广泛利用的Bug,也对部分物品的出现条件进行了修改,从很大的程度上增加了游戏的难度(个人的体会吧),但却提高了游戏的乐趣与挑战(不能刷箱子,无限TNT貌似是不能用了,也加入了新的Boss),也使得游戏的流程加长让玩家有足够的时间体验游戏带来的乐趣。

image

目前Terraria可以再XBOX 360/Live Arcade/PlayStation 3/Vita/Network/Android/iOS这些平台上运行,所以如果想体验这款佳作的话就马上行动吧!只要9.99美元即可拥有!买买买!咳咳,不好意思激动了。

Terrain在英语中是大地的意思Terraria就来源于此,而这一点在游戏中我们就可以明确的体会到。进入游戏后,我们需要做的是创建角色与地图,在Terraria中,角色与地图是相互独立的所以这为玩家们提供了很高的灵活度,也使得各个地图间的资源可以聚集起来使用为之后催生出来的建筑党玩家们提供了条件。

这里我们就从游戏流程中的开荒说起吧。(开荒是玩家们对游戏由最开始身无分文到最后装备毕业的一种说法)所谓的开荒简单概括的话就是,挖矿刷怪打BOSS,在对资源实现大量占有后一身神装,以达到单挑BOSS怒刷装备,走向人生巅峰的境界。最初的我们进入游戏后,我们所能使用的资源只有镐、斧子和木剑各一个,以及和我们一起出生在这个世界的悲情人物向导童鞋(由于游戏的某个设定向导需要死很多次,不过之后会复活)。开荒所要做的就是通过手头的工具砍树、挖矿、探索、打怪,通过获取的材料在向导那里得到合成表后,通过已有资源进行各种合成,以此生产高级的武器、工具与材料。期间可以通过建造居所吸引NPC居住来简化这一过程,直接购买或者出售物品。当觉得装备不错时通过挑战Boss获取更加高级的材料再进行升级。当然Boss有时会不期而至,这样阵亡也就不可避免了。值得一提的是当游戏进行到开荒中期时,我们在击败被大家称为肉山的Boss后,我们的世界会产生变化,会有更强的怪物和Boss出现同时也会产生新的矿物供我们使用,是我们也能变得更强与世界抗衡。最后在打败所有的Boss后获取其掉落的大部分物品,我们就可以算是装备毕业了,开荒流程也就算结束了。
(之后的所有视频建议全屏高清观看,有可能的话建议给浏览器安装屏蔽youku广告的插件)
探索&挖掘演示:


单挑Boss:

其实这样看来在没有等级与技能系统的支持下这样的游戏模式也许会让你感觉这是一款无聊的游戏,但Terraria的神级物品系统却彻底使得这款看似无聊的沙盒游戏让人玩起来根本停不下来。Terraria约有2336种物品,其中分为,装备、建材、武器、饰品、工具等,而这所有物品均可通过,采集、掉落、宝箱、合成、购买这些途径获得,单就武器而言就可以分为近战的刀、剑、矛等,远程的枪、弓箭、弩,法术类的法杖,还有链锤、回力标、召唤系法杖之类的东西,当然这样的分类是有些笼统的,之后我会专门介绍和分析武器系统。这样丰富的武器种类再加以,我们对地形的改造、各种有趣的Boss和这些Boss将会掉落的战利品的吸引,使得游戏中有着十分酣畅淋漓的战斗体验,尽管这是一款2D的平面像素沙盒游戏,但是其确实的战斗体验让我记忆深刻。另外,套装以及饰品所提供给玩家的特效也极高的提高了游戏的战斗体验。个人认为翅膀算是最成功的饰品之一了,翅膀的使用使得玩家不仅可以在地面以及搭好的场地中移动,还可以通过飞行与怪物周旋,大大提高了玩家在游戏中的自由度。
部分物品展示:

现在来说说之前提到的建筑党,由于地图与人物的分离,加以数量庞大的建筑用材料,使得玩家可以获取大量的资源用以建造,结合游戏中各种NPC出售的电路元件,建造各种各样的建筑为装备毕业后的玩家们提供了新的游戏途径。特别值得一提的是,游戏中的多种饰品也可以给玩家提供建造中的便利,让我们可以发挥想象力进行创造,以实现拥有华丽外观或特殊功能的建筑。在空余的时候堆出点好看的东西也算是一种放松吧。
image

上文中提到的电路结合地图中特定场景随机出现机关,也被玩家们利用为一些方便获取材料的途径,机关流刷怪、打BOSS之类的东西也都是很有趣的,有兴趣的玩家可以尝试一下,这里不再多说(因为太麻烦没怎么尝试过)。
刷怪神马的(地图是别的地方找的):

关于游戏机制,在游戏的过程中,我发现了一些有趣的东西。游戏的怪物的刷新是根据特定的生态环境按一定的概率刷出的。不同的生态环境有着不同的背景音乐和背景墙会刷出不动的怪物和植物。在熟悉游戏后,种种植物神马的我们就会发现,生态系统的构成实际和泥土的类型以及周围的植被有关。所以在游戏中我们可以人为地构建生态系统已获得我们想得到的植物或者想刷的怪,个人看来是很有意思的东西,根据这种机制我们可以构建种植园,进行药品的制作,或者自动刷怪之类的,这也不失为一种游戏方式。

最后来说一下游戏的多人模式。Terraria的多人联机有两种方式,其一是ip直连,另一种是通过游戏包中提供的Server程序建立服务器后进行连接。由于ip直连我这里没有成功过所一直用的是后者。当然这不是重点,下面说一下游戏体验吧,多人游戏在不开启PVP的情况下从模式上来说和单人几乎是一样的,不过早游戏中有伙伴与你一同打怪、探索、挖矿,一同挑战Boss建造家园却和单人是一种完全不同的体验,玩家可以通过人数上的优势克服在单人模式下一些很难跨越的障碍。而且不得不提的是,与伙伴共同游戏确实给玩家带来了更加强烈的激励去进行游戏,至少我的体验是这样的。所以多人游戏共同合作的模式给我们带来了更多的快乐,在我看来这一点做得非常成功。PVP目前还没有体验过,不过通过饰品与武器的组合进行灵活的对战想必也会很有意思吧。好吧既然没怎么体验我也就不瞎说了。
多人(虽然只有两个):

Terraria这部作品很值得朋友你去体验一番的。所以马上安装进入游戏吧~


  • 下期预告
  • Minecraft(大概是我的世界)

设计模式学习1--ABSTRACT FACTORY(抽象工厂)

最近开始了设计模式的学习,在这里做简单的记录
这里我用的是《设计模式:可复用面向对象软件的基础》这本书,用到的例子都是书中的内容
(基本就是抄书了= =。 咳咳 不要在意这些细节)
So,you got it~


这里看到的是创建型模式中的ABSTRACT FACTORY(抽象工厂)

抽象工厂可以为我们提供一个创建一系列相关相互依赖对象的接口,在我们使用时无需指定这些接口具体的类。(个人理解为C++的动态绑定技术)

在以下几种情况下我们可以使用ABSTRACT FACTORY模式

  • 一个系统要独立于它的产品创建、组合和表示时
  • 一个系统要由多个产品系列中的一个来配置时
  • 当你要强调一系列相关的产品对象的设计以便进行联合使用时
  • 当你提供一个产品类库,而只想显示它们的借口而不是现实

这里是结构图:

关于ABSTRACT FACTORY的应用,CE3实体系统似乎就是用的类似的模式实现,同时书上也给出了几个例子供大家参考这里就不做说明了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MazeFactory //AbstractFactory&ConcreteFactory
{
public:
MazeFactory();
virtual Maze* MakeMaze() const
{ return new Maze; }
virtual Maze* MakeWall() const
{ return new Wall; }
virtual Maze* MakeRoom(int n) const
{ return new Room(n); }
virtual Maze* MakeDoor(Room* r1, Room * r2) const
{ return new Door(r1, r2); }
};
class BombedMazeFactory : public MazeFactory //ConcreteFactory
{
public:
BombedMazeFactory();
virtual Room* MakeRoom(int n) const
{ return new RoomWithBomb(); }
virtual Room* MakeWall () const
{ return new BombedWall(); }
};

上面这段即为书上给出的例子,其中MazeFactory作为AbstractFactory的同时又充当了ConcreteFactory角色,而BombedMazeFactory则只是作为ConcreteFactory在其中。

在我们使用AbstractFactory时,使用MazeFactory的引用来创建实体,调用时我们只需通过不同的对象参数既可产生不同的实体。
具体而言就是这样一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Maze* MazeGame::CreateMaze (MazeFactory& factory)
{
//...
//这是一个生成迷宫的函数MazeGame,为这个游戏中的一个类
//这里只是说明问题所以具体的细节就不在这里显示了
//具体的代码在书中有提及(虽然也不是完整的)
}
MazeGame game;
MazeFactory factory;
//BombedMazeFactory factory;
//通过对这两行不同的类型的factory的调用,下面的函数会得到不同的结果
game.CreateMaze(factory);

描述中应该是有很多问题的,所以有错误的话希望大家指出作为纯正菜鸡再次感谢Orz

CE3备忘录2--枪械创建

关于备忘录:
这里主要是自己记录一些CE3(Cryengine3)的常用方法,包括代码编写以及API功能,如果有什么问题感谢指出,这是我的邮箱albstein2@gmail.com


这里简单介绍武器系统中的枪械创建及使用。

由我们自己创建的枪械类一般继承自CWeapon,当然CWeapon是基于接口类IWeapon创建的,不过在CE3枪械的大部分接口都在CWeapon中实现,所以这样的体系是很明智的选择(说得好像我很懂的样子Orz)

同上文这里的环境为windows7 x64下的VS2012

下面我们以一个基础的枪械来作为例子进行说明:

为了规范,我们将新建的DemoWeapon.h和DemoWeapon.cpp添加在/CryGameSDK/Item Files/Weapon Files/Weapons下

之后我们开始武器的编写

###1.打开DemoWeapon.h编辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef _DEMOWEAPON_H_
#define _DEMOWEAPON_H_
#include "Weapon.h"
class CDemoWeapon : public CWeapon //从Weapon继承,作为简单的模板这里就不实现其他功能了
{
private:
typedef CWeapon BaseClass;
public:
CDemoWeapon();
~CDemoWeapon();
//在需要的情况下可以在这里通过重写CWeapon的函数来实现特定功能
//当然这里不进行介绍,后面有机会的话会有例子Orz
};
#endif

###2.编辑DemoWeapon.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "StdAfx.h"
#include "DemoWeapon.h"
#include "Weapon.h"
CDemoWeapon::CDemoWeapon()
{
}
CDemoWeapon::~CDemoWeapon()
{
}
//这里没什么好说的了就是简单的定义(就好像什么都没有一样)

###3.注册武器

当文件编辑完成后我们进行武器的注册,CE3的武器在GameFactory.cpp中注册(找不到的话可以再VS的资源管理器中搜索)

打开GameFactory.cpp在开头的相应位置(就是武器啥的放到一起比较规范)添加

1
#include <DemoWeapon.h>

之后找到

1
2
3
4
REGISTER_FACTORY(pFramework, "Weapon", CWeapon, false);
//后面还有很多找到这个就行了
//在后面添加
REGISTER_FACTORY(pFramework, "DemoWeapon", CDemoWeapon, false);

这样武器就注册成功了

###最后找到GameSDK\Scripts\Entities\Items\XML\Weapons并新建DemoWeapon.xml简单起见复制rifle.xml之中的内容找到



<item name=”Rifle” class=”Weapon” category=”primary” priority=”12” weaponParams=”1”>


将”Rifle”和”Weapon”都改为”DemoWeapon”
再找到



<param name=”display_name” value=”@mp_eRifle”/>


将”@mp_eRifle”改为”@mp_eDemoWeapon”

如果无误的话编译运行程序就可以在编辑器的Item中找到我们注册的”DemoWeapon”,拖到游戏中就可以使用了。

到这里注册武器的流程就结束了,其他的功能新建或者重写成函数来完成,是武器的功能丰富就靠想象力和API的熟练程度了,这里之后也许会有例子吧。

CE3备忘录1--弹头创建

关于备忘录:
这里主要是自己记录一些CE3(Cryengine3)的常用方法,包括代码编写以及API功能,如果有什么问题感谢指出,这是我的邮箱albstein2@gmail.com


这里介绍在CE3中子弹弹头的创建及使用。

首先CE3中的弹头的创建一般基于抛射物,即继承自CProjectile,当然根据需要有时会用到多重继承调用其他的资源,不过这里作为基本的介绍所以不予考虑。

这里的环境为windows7 x64下的VS2012

这里我们以一个可以为目标物体造成固定伤害的弹头为例子介绍:

首先在VS的资源管理器中的/CryGameSDK/Item Files/Weapon Files/Projectiles目录下创建文件HitBullet.h和HitBullet.cpp

###打开HitBullet.h编辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#ifndef _HITBULLET_H_
#define _HITBULLET_H_
//保证文件只编译一次这里很重要希望大家注意,之后我就不再说明了
#include "StdAfx.h"
#include "Projectile.h"
class CHitBullet : public CProjectile
{
typedef CProjectile BaseClass;//方便使用基类资源,不过这里我们用不到实际操作时可以删掉这行
public:
CHitBullet();
~CHitBullet();
virtual void HandleEvent(const SGameObjectEvent &);
//事件处理函数,继承自CProjectile,由于该类可能被作为基类调用故声明为虚函数
//在这里我们通过重写这一函数来实现效果
private:
float damage;
//手动设置伤害值,这里通过函数调用伤害其实可以从武器的xml中获得
//这里为了方便直接设置
};
#endif

###保存后打开HitBullet.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "StdAfx.h"
#include "HitBullet.h"
#include "Game.h"
#include "GameRules.h"
#include "Actor.h"
CHitBullet::CHitBullet()
:damage(100) //将伤害初始化
{
}
CHitBullet::~CHitBullet()
{
}
void CHitBullet::HandleEvent(const SGameObjectEvent &event)
{
CProjectile::HandleEvent(event); //先调用父类的HandleEvent完成必要操作
if(event.event == eGFE_OnCollision) //判断实体事件是否为碰撞事件,若不是则不进行操作
{
EventPhysCollision *pCollision = (EventPhysCollision*)(event.ptr); //强制转换类型,在CE里很常见
if(!pCollision) //如果指针为空则不进行操作
return;
IEntity *pTarget=pCollision->iForeignData[1] ==PHYS_FOREIGN_ID_ENTITY ? (IEntity*)pCollision->pForeignData[1] : 0; //获取与弹头发生碰撞的实体.(PHYS_FOREIGN_ID_ENTITY表明是实体)
if(pTarget) //如果实体获取成功进行如下操作
{
CActor *pActor=(CActor*)g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(pTarget->GetId()); //获取角色
EntityId targetId=pTarget->GetId();
CryLogAlways(pTarget->GetClass()->GetName()); //CryLogAlwasy是用的非常多的Debug函数,用法跟printf一样.默认类型为string,%d,%f啥的你都懂的
HitInfo hitInfo(m_ownerId ? m_ownerId : m_hostId, targetId, m_weaponId,
damage, 0.0f, 0, pCollision->partid[1],
m_hitTypeId, pCollision->pt, Vec3(0,0,1), pCollision->n); //Hitinfo是个结构体,定义了子弹(或别的什么)造成的伤害的各种参数
hitInfo.knocksDown=false;
hitInfo.knocksDownLeg=false;
hitInfo.remote = IsRemote();
hitInfo.projectileId = GetEntityId();
hitInfo.bulletType = m_pAmmoParams->bulletType;
hitInfo.knocksDown = CheckAnyProjectileFlags(ePFlag_knocksTarget) && ( damage > m_minDamageForKnockDown );
hitInfo.knocksDownLeg = m_chanceToKnockDownLeg>0 && damage>m_minDamageForKnockDownLeg && m_chanceToKnockDownLeg>(int)Random(100);
hitInfo.penetrationCount = 0;
hitInfo.hitViaProxy = CheckAnyProjectileFlags(ePFlag_firedViaProxy);
hitInfo.aimed = CheckAnyProjectileFlags(ePFlag_aimedShot); //上面一长串都是初始化hitinfo里的玩意,这些记住就好,记不住就直接复制粘贴...
g_pGame->GetGameRules()->ClientHit(hitInfo); //处理这个伤害事件
}
Destroy(); //销毁弹头.
}
}

上述内容完成后,我们武器本体代码就完成了,接下来我们将新建的弹头加入到武器系统中

打开/CryGameSDK/Item Files/Weapon Files下的WeaponSystem.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include "StdAfx.h"
#include "Game.h"
#include <IEntitySystem.h>
#include "Rocket.h"
...//省略一部分
#include "HitBullet.h"//包含武器的头文件
...//省略一部分
CWeaponSystem::CWeaponSystem(CGame *pGame, ISystem *pSystem)
...//省略初始化
{
...//省略一部分
// register projectile classes here
REGISTER_PROJECTILE(Projectile, CProjectile);
REGISTER_PROJECTILE(Bullet, CBullet);
REGISTER_PROJECTILE(KVoltBullet, CKVoltBullet);
REGISTER_PROJECTILE(Rocket, CRocket);
REGISTER_PROJECTILE(HomingMissile, CHomingMissile);
REGISTER_PROJECTILE(C4Projectile, CC4Projectile);
REGISTER_PROJECTILE(Chaff, CChaff);
...//省略一部分
REGISTER_PROJECTILE(HitBullet, CHitBullet);//将刚才写好的弹头类在这里注册,其他的地方不用管,这样就将写好的弹头注册进了武器系统
m_pPrecache = gEnv->pConsole->GetCVar("i_precache");
CBullet::EntityClass = gEnv->pEntitySystem->GetClassRegistry()->FindClass("Bullet");
m_pGame->GetIGameFramework()->GetILevelSystem()->AddListener(this);
m_linkedProjectiles.reserve(LINKED_PROJ_MAP_RESERVE);
m_freePoolHandlers.insert(TWeaponComponentPoolFreeFunctions::value_type(CMelee::GetWeaponComponentType(), &FreeWeaponComponentPool<CMelee, CMelee>));
m_listenersLock = 0;
}
...//省略其它成员函数

之后在CE3目录下的GameSDK\Scripts\Entities\Items\XML\Ammo中新建文件HitBullet.xml
打开后拷贝该目录下的RifleBullet.xml中的内容

    
        <ammo name="HitBullet" class="HitBullet">
        <!--将第一行改为这样-->
        ...<!--略-->
    

接着新建一种使用该弹头的武器,我们一Rifle为模板改动
在GameSDK\Scripts\Entities\Items\XML\Weapons中新建文件HitBullet.xml
打开后拷贝该目录下的Rifle.xml中的内容

    
    <item name="HitBullet" class="HitBullet" category="secondary" priority="13" weaponParams="1">
      <params>
        <param name="display_name" value="@mp_eHitBullet" />
           ...<!--略-->
      </params>
        ...<!--略-->  
      <ammos>
        <ammo name="HitBullet" extra="1" amount="1" minAmmo="1" capacity="0" GAME="SP" /><!--改动-->
        <ammo name="HitBullet" extra="45" amount="9" capacity="45" GAME="MP" /><!--改动-->
      
      <firemodes>
        <firemode type="default">
          <fire>
            <param name="hit_type" value="RifleBullet" />
            <param name="ammo_type" value="HitBullet" /><!--改动-->
            ...<!--略-->
          </fire>
          ...<!--略-->
    

之后编译运行就可以在边栏看到武器了,添加后即可使用。

游戏评测5——Trine2

由于停电,存档丢失以及新版本的改动,所以拖到现在才写这篇Orz
好的咱们开始OwO


《三位一体2(Trine2)》是由Frozenbyte研发、ATLUS发行的横向滚动条动作游戏《三位一体》的续作,其中有法师、盗贼、骑士等三位主角,玩家可随时从中切换操纵的对象,体验完全不同的游戏风格,也可和好友分别操作不同角色,体验共同闯关的乐趣。

image

该游戏于2011年12月9日上市,大家可以在PC/PS4/PS3/Xbox360/MAC/Android进行游戏。

安装完成后,做完简单的设置即可开始游戏。最初呈现给玩家的就是优美的主旋律,于我而言,这与游戏的画面综合起来便是Trine这一系列作品的特色之一。游戏中玩家将在各种精心设计的场景中进行解谜、战斗,再伴以恰到好处的配乐,愉快的玩耍就这样开始了。

image

Trine2将收集元素与经验系统相结合,同时角色的升级又关系着通关的顺利程度,所以散落在地图各处的经验就显得尤为重要。正如多数的评论所言,游戏物理效果确实是Trine系列作品的核心,在解谜中扮演着不可缺少的作用。这样的系统给了玩家一种正面的激励,你所获得到的经验越多,你就能更轻易地获取更多的经验,这也许是一些喜欢全收集的玩家的福音吧。

image

就PC而言,WASD以及鼠标的传统键位,让这款游戏极易上手。所以单从操作来讲Trine2是没有什么难度的,这款游戏的难度在于一些令你绞尽脑汁的解谜。当然,作为一款解谜游戏这是应该的,这同样也是游戏的魅力所在,当你通过苦苦思索并终于拿到经验或者通过关卡,所带来的喜悦~玩过你就明白了。游戏中法师的技能学习是解谜的关键,拖动物体以及构建机械箱子和机械板,都是解谜中必不可少的部分所以想要轻松通关,主要升级法师的技能是个不错的选择。其次盗贼和骑士是战斗中的主要输出,(当然法师对敌人的抓取配合场景也是不错的选择)在行动方面盗贼的钩锁起着十分重要的作用,对于通过障碍起着重要的作用。而骑士在我个人看来是个有些鸡肋的角色,由于法师和盗贼的工程作用,以及盗贼在升级弓箭后的伤害输出,使得本应作为肉盾的骑士没有什么太好的作用,除了少数场景需要使用到盾牌来通过,其作用在游戏的进行中微乎其微,当然这是我个人的观点如有雷同不胜荣幸~

image

值得一提的是在游戏的较早版本,法师可以通过创造机械板来完成飞行,使游戏简单不少,但在后来的更新中这似乎是被作为一个bug和谐掉了,同时新版本也在原有的剧情之后又加入了哥布林战争一章,其优秀的美术表现为本作增色不少,同样,更多的谜题等待你去解答。

这里是我做的一段演示:

这里是我优酷空间其中有几段哥布林战争的流程,比较瞎不过可以看看。

Trine2这样一款游戏,将解谜与各人物的技能巧妙的结合起来,同时有营造出了其所必要的精致场景,无处不在的解谜使得游戏充满挑战,排除强迫症患者想要全收集,该游戏的重复可玩性不高,但其优秀的音乐及场景却可以不断的让你去欣赏。在茶余饭后体验这样一款精致的解谜游戏是个不错的选择~

ps:关于游戏的多人模式,由于我没有体验过所以这里不再多说。


  • 下期预告
  • Terraria

游戏评测4——Fez

致我的那些献出去的膝盖Orz

image

《Fez》是由Polytron Corp.大神耗时五年开发的,曾获得GDC 2012最佳独立游戏奖项。这是一款极具创意的利用视角的转换来改变关卡舞台,结合2D和3D的关卡的平台动作游戏全面展现游戏清新画面、复古音效背后创意玩法所能能给玩家带来的无限乐趣。

该游戏可以在XBOX 360 PC PS3 PSV上进行,于2012年04月13日上市发行。

IGN给出了9.5分的评价,这款看似精致又可爱的游戏实则暗藏杀机,不要问我为什么,玩过你就懂了。

  • 游戏演示

之后的内容包含剧透信息 = =

关于Fez我的第一印象定格在了系统崩溃重启的那张画面上。游戏开始时我们的主角Gomez被要求去见一个老头,接着就是系统给我们的简单教学,当爬到地图顶端见到老头并进行对话之后,我们将会看到之后将要一直去寻找的正立方体(当然还有反立方体等着你去发现)。随着画面的旋转这款看似2D的游戏展现出了它的真实3D面目,同时立方体中落下一个萌萌的红色小帽子,找我们的老头也带着同样的帽子,这时我们就获得了旋转世界的能力,系统会提示你怎么进行。(具体的感觉用语言不太好形容,下面是我录的一段演示视频)之后中画面开始花屏,然后游戏重启(Ps:我真的以为电脑崩溃了……),再次载入后我们就已经带着这顶小红帽了,一周目的冒险正式开始。

image

游戏中会有一个水晶一样的伙伴一直给我们提示如何进行游戏,当然一周目的目标就是收集正立方体来打开通向各个世界的门,然后继续收集正立方体,以还原破碎的魔方,使得我们所在的世界恢复稳定,这里的难度主要就在于通过转换视角以及进入通向不同世界的各种门进行黄色立方体的收集,视角的转换可以算是整个游戏的亮点与核心,这是一种从未有过的感受,个人感觉非常棒。同时游戏略萌的画风清新的配乐以及环境音效的组合,让玩家在游戏过程中也可以感到些许轻松,或多或少忘却刚刚战死的脑细胞Orz。

游戏中可以通过Tab来查看物品收集的进度,当我们收集够32块正方体时(任意),就可以打开一周目结束的门。通过之后游戏再次重启,我们在与开头相同的地方获得3D眼镜,出家门后开始真正凶残的二周目,当然你这个时候查看游戏进度的时候就已经超过100%当然这个游戏需要达到210%才算结束,所以一周目仅仅是开始。

image

前文中我们一直说的正立方体也算是收集完成,但是却无法复原魔方,原因就是二周目我们需要继续收集反立方体,而这也是在我看来这个游戏真正难的地方。关于反立方体的收集,每一个都是条谜题,从steam成就系统中的提示,到宝箱中的藏宝图,再到二维码解密、声音编码破译、还有用获得的3D眼镜查看场景中写下的密码,其难度简直令人发指……不过也正是这样多样化的解密缓解让我根本无法停下进行这款游戏,更让我惊讶的是,游戏中竟然有着自己的数字与字母代码,并且留下了线索给玩家去破解,当时真的算是跪在屏幕前完成这个游戏的。游戏进行中一次次的发现自己智商捉急,在攻略、计算器、手机、草稿纸的协助下完成了二周目的流程。当时的心情简直是激动……然后就发现游戏居然还有东西要收集,摸了摸自己的膝盖扑通就跪在了地下。QAQ这游戏好难啊……

image
image

于是最后一块方块看着攻略也没搞定……啊,就这样吧。

image

总之这款游戏在我眼里简直算是神作了,以我的膝盖发誓,绝对值得一玩。

这里补一张完整的世界地图,当然不想被剧透的话就不要看了
image


  • 下期预告
  • 三位一体2(Trine2)

游戏评测3——堡垒(Bastion)

image

Bastion《堡垒》是由独立工作室Supergiant Games制作,其团队作为曾参与设计《命令与征服》系列作品,可以说是有着丰富的经验(好吧看到这里的时候吓了一跳,这游戏怎么和红警还有关系)。游戏有着很强的兼容性,可以在IOS/Windows PC/Linux等平台运行,所以说想玩的话压力不大,就算你是开源党,你依旧可以在你的Linux或者iPhons上运行这款游戏。

在一个中年大叔的旁白声音下,作为主角Kid苏醒在了灾难过后的世界中,整理了一下凌乱的头发清理了一下站在门牙上的韭菜,拎起大锤走向不断浮现出的地面,再这样一个分崩离析的世界中拯救幸存者(包括旁白大叔)、收集世界碎片、欺负着一路不断刷出的各种怪兽敌人,不断完善这座天空堡垒,最终走向由你来选择的人生巅峰(貌似最后是成功脱团了)。

当然,以上内容纯属虚构,如有雷同纯属巧合(这样就不算我剧透了吧,哈哈哈哈)。

  • 感觉这个画风很不错
    image
    image

我大概是花了8个小时一周目通关,一周目中有着不能重复关卡的设定,个人来讲是比较喜欢的,但是想要一周目全收集的童鞋们需要注意下这一点。一周目之后会在选项中多出带积分的挑战模式,有兴趣的话可以试试,个人感觉这样刷分的系统不应该出现在这么感人的故事之中,不过既然一周目都过了应该也问题不大。

当刚开始打开游戏的时候,第一眼的载入画面以及优美的bgm给我留下了很深的印象,一直到游戏开始,一周目通关,整个游戏清新的画风和应景的bgm始终让你觉得将这款游戏进行下去是一种享受。

游戏讲述的故事十分给力,希望大家可以用心去体验,当旁白开始讲述故事时,你就开始了冒险的旅程,伴随着故事的发展以及游戏本身所提供的流畅操作和畅快的打击感,让你根本停不下来去将游戏通关(本人手残中间死了几次Orz)。

从游戏系统来说,游戏有着丰富的武器以及每种武器通过收集要素来进行的升级系统,所以整个游戏中有着很多种选择给你去将游戏进行下去。其次是游戏的难度和buff系统,这些设置通过堡垒上的神庙和酒馆两座建筑得以体现,玩家可以通过供奉和饮酒来设置下一个关卡中的难度和所携带的buff,感觉是很巧妙的设计。同时游戏中自带的成就系统也在堡垒中以建筑的形式体现,以素描画的形式来标注成就的进度,具体细节你玩了就知道~ 当然,需要说明的是这个段落中的操作都是在游戏中堡垒的各个建筑中完成,而建筑却需要你通过寻找碎片来复原,所以个人认为整个游戏都设计得非常巧妙。

当然短短数语无法将这作品所完整的展现,没有提到的部分还是请各位去亲身体验后自己来感受吧~

image

  • 一周目通关纪念

  • 逗比演示视频OwO


ps:上面是我的优酷空间,演示都会传上去的,无聊的话可以看看


  • 下期预告
  • FEZ
  • PS:玩过的最高端的游戏Orz

关于跳票,感觉自己语言表达能力很差劲所以之前写好的东西删了改搞了好多遍。然后,想了下貌似没多少人会看吧,所以以后主要会以自己的看法为主吧,就这样写咯,这两天会把没发的都发出来~

游戏评测2——风之旅人(Journey)

image
风之旅人(Journey)由游戏开发者陈星汉,PS3独占,于2012年3月13日发售。IGN评测

与其说这是一款游戏倒不如称之为艺术品,无论是我们能直观感受到的场景和配乐还是整个游戏的叙事,都可以用完美来形容。以至于在第一次游戏流程结束的时候自己已不觉泪流满面。游戏所带来的震撼与感动在我进行游戏的同时不停的刺激着我的神经,并在随后结尾旅途结束时释放出来,那感觉至今难忘。

image

游戏中玩家扮演一位身着红色斗篷在沙漠中行走的旅行者,目的是前往视野中那座高耸入云的山。游戏开始后玩家漫步在一望无际的沙漠中,当走向一处高高的沙丘之后,天边高耸的山峰出现在眼前并逐渐清晰,之后屏幕显示出游戏标题Journey,直到通关后的制作人列表,这是整个游戏中出现唯一的文字的。整个游戏没有任何对话,玩家只需用心感受这故事,这也是陈星汉游戏的独特之处。

image

游戏的操作异常简单,除了摇杆操作方向以外只有吟唱和漂浮两个键,简单的操作却给了玩家灵动的操作体验,游戏并没有传统游戏中常见的积分或者等级系统,整个游戏所想体现的精神在过程中逐步渗透给玩家,这所谓的精神也是整个游戏的核心体验所在。

游戏分为几个篇章,每一章都有着风格迥异的配色以及音乐风格,紧张、闲适、平和、压抑,仅通过画面和音乐玩家就能体验到这所有的感受,而这都是由仅仅2小时左右的游戏流所程带来的。

除了两小时的流程所讲述的故事之外,游戏的另一个核心体验来自于其多人游戏的设计。当你的play station联网的时候在游戏的每一章都有可能出现另一个玩家以你一同完成这一章,在孤独的旅途中伙伴的出现,使得游戏的体验又达到了新的层次。熟练的玩家会带领新手玩家解决游戏中的困难,并向其展示游戏中隐藏的情节,当一个玩家脱节时他们会相互等待共同完成这段旅途。这是一种很难用语言形容的感觉。游戏中的某些设计也是必须要在两个玩家的合作下才可以完成,这也是开发者对人与人之间互相帮助的一种激励,游戏激发了人性中的善,这种激励道德层面提高了游戏的价值。

image

当游戏结束时新手玩家在最后一个场景的沙画出图案来表示他对另一为玩家的感谢,如何不为之感动。

Journey就是人生的写照,在生命的旅途中我们会有种种诸如成功、失败、喜悦、平和、悲哀的经历,路途中我们都是孤独的,偶尔有人相伴但终会走向终点。也许这就是制作人想传达给我们的生命之旅。

image

##尾声:
到现在还记着当流程结束时整个人拿着手柄呆坐在屏幕前的感受,难以言表的感动。之前听说很多人为了这款游戏买了PS3,体验过后我个人是愿意的,这款游戏适合所有人去体验,所以如果有条件的话2小时的旅途绝对不会让你失望。


  • 下期预告
  • 堡垒(Bastion)

游戏评测1——古墓丽影9(Tomb Raider 9)

以下内容均为个人观点,要是写的不好
你来打我啊(╯‵□′)╯︵┻━┻

##说明:
《古墓丽影9》是由Crystal Dynamics开发,Square Enix负责发行的跨平台系列动作游戏,2013年3月开始发售。

这算是我玩过的第一款AAA游戏吧,第一遍流程是13年玩的,当时大概是84%通的关吧,确实是留下了很深刻的印象,包括场景布置人物建模以及优秀的主线故事。前段时间Steam夏日特卖打折入了年度版,又通了一遍,认真收集了几乎所有的要素98%完成度通关(然后电脑坏了……存档丢失WTF)。

个人认为是一部十分优秀的作品,一改前作中Lara Croft双枪大妈的形象,给我们展现出了初出茅庐的Lara是如何完成她的第一次冒险以及其成长的历程。IGN的编辑为这部作品打出了9.1分的高分。IGN评测

Lara Croft

##评测:

这里说明一下,之后的内容涉及剧透,还没有玩过的朋友……啊,你们懂的

故事开始Lara一行人所乘坐的坚忍号在,即将登陆目的地邪马台时遭遇海难,生还的船员们到达了海滩,Lara却在海滩被打晕掳走挂在类似祭坛山洞里,游戏开始,玩家通过操作将自己从天花板上点燃摇下来。之后是一段对于游戏控制简单教程,包括游戏中解谜元素的核心,求生本能——在触发后会高亮周围要用到的物体帮助你解决谜题。通过后是一段QTE逃离山洞到达游戏的第一个开阔场景,光影的效果个人感觉做得很好,呼应剧情,接着天边出现Tomb Raider的标志,游戏就算正式开始了。

游戏中,玩家会与Lara一起一件件获取武器,收集物品,在这过程中通过残料的收集以及剧情的推进可以一步步解锁并升级自己的武器,同时获取的经验也可以提高等级,习得技能以在小岛中求得并生存解救自己的同伴。如之前所提到的游戏中的求生本能这一设定可以帮助你解谜,同时也为像我这种强迫症玩家在收集要素和完成挑战时提供了便利(IGN的编辑似乎觉得这东西没啥用= =!)。游戏中需要收集的文献日记等,使得剧情更加完备,再加上半开放世界中的种种挑战,为大部分玩家在通关后再次进行游戏提供了一个十分有力的理由。

相对与前作,Tomb Raider 9降低了解谜的难度,同时加强了战斗要素,再配上武器系统的一步步解锁及升级使得游戏的战斗十分的吸引人。在游戏中作为Lara的玩家会依次获取弓箭登山镐手枪步枪霰弹枪(顺序大概是这样吧)。同时随着武器的升级每种武器会获取新的攻击方式,再配以游戏中一场场由剧情触发的酣畅淋漓的战斗,将各种武器的优劣势均得以体现。无论是暗杀、突突突还是近战,这几种武器的组合均给你提供了合适的方案,使玩家以其所想要的方式完成战斗,再配以优秀的打击感以及场景中爆炸物、掩体的布置,使得战斗在很爽快的同时又给了玩家多样化的选择。个人感觉这一块做得十分优秀。

当然Tomb Raider 9虽然降低了解谜的难度,但是古墓的探索依旧是很让人难忘的部分(我不会说这是因为每个古墓会给很多的经验以及游戏完成度),巧妙地谜题设计,以及场景的布置可以说依旧给力。

在布景方面我觉得有很多的场景设计的非常美,包括海滩的夕阳,最后前往废墟时雷电交加所带来的震撼,信号塔顶赏心悦目的鸟瞰画面等,这些都是需要玩家去亲身体验的,本来有很多的截图结果忘记上传了。之后如果再玩的话补回来。

配乐及CG个人觉得很好但是不太懂就不瞎说了。

最后就是多人游戏方面,IGN的建议是你就当他不存在好了233,我这里由于网络的问题没有尝试所以请参照IGN建议。

总体来讲游戏有着十分优秀的剧本以及人物刻画,再配以环境音乐以及战斗,有着很强的代入感,与Lara一同成长最后完成对自己以及朋友拯救,玩起来十分带感。结尾最后一场战斗,Lara举起双枪射杀BOSS也与前作相呼应,把这部前传与后面的历程所联系。这部作品值得一玩,在这里强烈推荐。

##尾声:

今年的E3发布会中Tomb Raider下一部作品的发行决定已确定,十分期待啊。Lara Craft的冒险还将继续~

Ps:关于评测我也是第一次写,这里主要是我个人对游戏的看法,所以如果有什么问题建议的话欢迎交流。


  • 下期预告
  • 风之旅人(Journey)

winsock编程(1)——基于Tcp的Echo

最近整理一下之前写过的一些东西,就从这里开始吧~
image

服务端结构:

1.使用socket函数获取套接字

1
2
echo_soc = socket(AF_INET,SOCK_STREAM,0);
//从socket的库里随机获取,两次调用相同程序可能会得到相同的套接字

echo_soc的值可以看做服务器端打开的一个通道

2.使用bind函数将一本地地址与套接口捆绑

1
2
result = bind(echo_soc,( struct sockaddr*)&serv_addr,sizeof (serv_addr));
//当result = SOCKET_ERROR时,绑定出现错误,退出程序

3.listen监听端口

1
2
listen(echo_soc, SOMAXCONN);
//这里SOMAXCONN为最大合理值(不太清楚啥意思)

附:

  • msdn原文第二个参数:
  • Maximum length of the queue of pending connections. If set to SOMAXCONN, the underlying service provider responsible for socket s will set the backlog to a maximum reasonable value. There is no standard provision to obtain the actual backlog value.

4.监听开始后服务端启动

通过accept函数接受套接字用recv和send函数收发数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
while(1)
{
acpt_soc = accept(echo_soc,( struct sockaddr*)&clnt_addr,&addr_len);
//ip在clnt_addr中存储?
if(acpt_soc == INVALID_SOCKET)
{
printf( "[Echo Sever] accept error: %d\n" ,WSAGetLastError());
break;
}
result = recv(acpt_soc, recv_buf, ECHO_BUF_SIZE,0);
if(result > 0)
{
recv_buf[result] = 0;
printf( "[Echo Sever] receives: \"%s\",from %s\r\n" ,
recv_buf, inet_ntoa(clnt_addr.sin_addr));
result = send(acpt_soc, recv_buf, result,0);
}
closesocket(acpt_soc);
}
//while(1)死循环持续接受客户端的套接字,当接收成功时打印收到信息与客户端ip地址,然后用send发送收到的信息。

关于服务端先到这里= =

下面是服务端完整源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32")
#define ECHO_DEF_PORT 7
#define ECHO_BUF_SIZE 256
int main(int argc,char **argv)
{
WSADATA wsa_data;
SOCKET echo_soc = 0,
acpt_soc = 0;
struct sockaddr_in serv_addr,
clnt_addr;
unsigned short port = ECHO_DEF_PORT;
int result = 0;
int addr_len = sizeof(struct sockaddr_in);
char recv_buf[ECHO_BUF_SIZE];
if(argc == 2)
port = atoi(argv[1]);
WSAStartup(MAKEWORD(2,2),&wsa_data);
echo_soc = socket(AF_INET,SOCK_STREAM,0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = INADDR_ANY;
result = bind(echo_soc,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
if(result == SOCKET_ERROR)
{
printf("[Echo Sever] bind error: %d\n", WSAGetLastError());
closesocket(echo_soc);
return -1;
}
listen(echo_soc, SOMAXCONN);
printf("[Echo Sever] is running ……\n");
while(1)
{
acpt_soc = accept(echo_soc,(struct sockaddr*)&clnt_addr,&addr_len);
if(acpt_soc == INVALID_SOCKET)
{
printf("[Echo Sever] accept error: %d\n",WSAGetLastError());
break;
}
result = recv(acpt_soc, recv_buf, ECHO_BUF_SIZE,0);
if(result > 0)
{
recv_buf[result] = 0;
printf("[Echo Sever] receives: \"%s\",from %s\r\n",
recv_buf, inet_ntoa(clnt_addr.sin_addr));
result = send(acpt_soc, recv_buf, result,0);
}
closesocket(acpt_soc);
}
closesocket(acpt_soc);
WSACleanup();
return 0;
}

客户端结构:

1.使用socket函数获取套接字

1
echo_soc = socket(AF_INET,SOCK_STREAM,0);

2.使用connect函数与服务器进行连接

1
2
result = connect(echo_soc,( struct sockaddr *)&serv_addr,sizeof (serv_addr));
//result为0时连接成功

附:

  • 第二个参数为一结构体指针其中包含服务器的ip地址端口号等信息,在本程序中端口号在宏定义中确定,ip地址从命令行获得。

3.使用send与recv函数进行数据收发

1
2
result = send(echo_soc,test_data,send_len,0);
result = recv(echo_soc,recv_buf,ECHO_BUF_SIZE,0);

附:

1
2
3
4
5
6
7
if(result > 0){
recv_buf[result] = 0;
printf( "[ECHO Client] receives: \"%s\"\r\n" ,recv_buf);
}
else
printf( "[ECHO Client] error: %d.\"\r\n" ,WSAGetLastError());
//result在后续函数中作为判断条件,大于0说明成功收发send、recv函数成功执行,则打印数据。

  • 最后关闭套接字,结束winsock资源既结束。

下面是客户端完整源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <stdio.h>
#include <stdlib.h>
#include <WINSOCK2.H>
#pragma comment(lib,"ws2_32")//winsock使用的函数库
#define ECHO_DEF_PORT 7//连接的默认端口
#define ECHO_BUF_SIZE 256//缓冲区大小
int main(int argc, char **argv){
WSADATA wsa_data;
SOCKET echo_soc = 0;//socket句柄
struct sockaddr_in serv_addr;//服务器地址
unsigned short port = ECHO_DEF_PORT;
int result = 0, send_len = 0;
char *test_data = "Hello World!", recv_buf[ECHO_BUF_SIZE];
if(argc < 2){
printf("input %s server_address [port]\n",argv[0]);
return -1;
}
if(argc >2)
port = atoi(argv[2]);
WSAStartup(MAKEWORD(2,2),&wsa_data);//初始化winsock资源
send_len = strlen(test_data);
//服务器地址
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
if(serv_addr.sin_addr.s_addr == INADDR_NONE){
printf("[ECHO] invalid address\n");
return -1;
}
echo_soc = socket(AF_INET,SOCK_STREAM,0);
result = connect(echo_soc,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
if(result == 0){//连接成功
result = send(echo_soc,test_data,send_len,0);
result = recv(echo_soc,recv_buf,ECHO_BUF_SIZE,0);
}
if(result > 0){
recv_buf[result] = 0;
printf("[ECHO Client] receives: \"%s\"\r\n",recv_buf);
}
else
printf("[ECHO Client] error: %d.\"\r\n",WSAGetLastError());
closesocket(echo_soc);
WSACleanup();
return 0;
}

附录:
关于socket(套接字)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct in_addr {
union {
struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { USHORT s_w1,s_w2; } S_un_w;
ULONG S_addr;
} S_un;
#define s_addr S_un.S_addr /* can be used for most tcp & ip code */
#define s_host S_un.S_un_b.s_b2 // host on imp
#define s_net S_un.S_un_b.s_b1 // network
#define s_imp S_un.S_un_w.s_w2 // imp
#define s_impno S_un.S_un_b.s_b4 // imp #
#define s_lh S_un.S_un_b.s_b3 // logical host
} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;
//第一个结构体,既
//struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
//用于存储ip地址,其中s_b1到s_b4存储的为ip地址的四节

就这么多了=。=