存档

2014年8月 的存档

【cocos2d-x 3.0 – JS -Mac配置篇】

2014年8月27日 没有评论

一、准备工作

Windows配置篇请见

http://cn.cocos2d-x.org/tutorial/show?id=1396

1.安装python 

2.下载Android SDK Android

3.下载Android NDK

4.下载Cocos2d-JS 3.0版本,解压,这里博主解压到了Apache根目录下面(当然需要你安装配置了Apache,这里不再多说),这是因为用过Cocos2d-HTML5的人都知道,想用浏览器查看效果的话,需要放在服务器根目录下,如果不想用浏览器查看,可以任意位置解压,如图:

MAC版本自带了Apache

启动Apache

打开“终端(terminal)”,输入 sudo apachectl -v,(可能需要输入机器秘密)。如下显示Apache的版本

【cocos2d-x 3.0 - JS -Mac配置篇】

可以自己试试

放到根目录下,/Library/WebServer/Documents/”下,这就是Apache的默认根目录。

【cocos2d-x 3.0 - JS -Mac配置篇】

 

工具下载:【cocos2d-x 环境配置-Mac配置篇】 里面都有

 

二、添加环境变量

打开终端配置环境变量,输入以下命令:

vim ~/.bash_profile
export PATH=$PATH:/Users/yourmacname/adtformac/sdk/tools

export PATH=$PATH:/Users/yourmacname/adtformac/sdk/platform-tools

export PATH=$PATH:/Users/yourmacname/android-ndk-r9b/

export ANDROID_NDK_ROOT=/Users/yourmacname/android-ndk-r9b/

export ANDROID_SDK_ROOT=/Users/yourmacname/adtformac/sdk

export NDK_ROOT=/Users/yourmacname/android-ndk-r9b/

# Add environment variable COCOS_CONSOLE_ROOT for cocos2d-x
export COCOS_CONSOLE_ROOT=/Library/WebServer/Documents/cocos2d-js-v3.0-rc2/tools/cocos2d-console/bin
export PATH=$COCOS_CONSOLE_ROOT:$PATH
    
# Add environment variable ANT_ROOT for cocos2d-x
export ANT_ROOT=/Users/yourmacname/apache-ant-1.9.3/bin

添加修改如上路径;红色部分必须重点查看!第一条放在Apache下面,第二条ANT地址,必须加/bin。

如果都配置成功了,好的,终于可以安装了;

打开CocosJS目录下的setup.py;

如果你们上面的配置没有错误,这里就是一闪而过,要是有配置错误的地方,便会卡住;

【cocos2d-x 3.0 - JS -Mac配置篇】

 

安装通过!

cocos new hello -l js

创建工程!

【cocos2d-x 3.0 - JS -Mac配置篇】

创建成功会在cocos2d-js-v3.0根目录生成 对应“hello”的工程文件夹

打开safari 输入对应路径 http://localhost/cocos2d-js-v3.0-rc2/hello/

即可看到如下画面:

【cocos2d-x 3.0 - JS -Mac配置篇】

 

分类: 未分类 标签:

九宫格战斗架构解析

2014年8月26日 没有评论

战斗部分总模块划分
一,战斗单机版框架
细分框架包含如下系统和技术点:
1,地图循环系统;
2,精灵系统(2方向4动作);
1),精灵动作状态机,总精灵父类,玩家精灵子类,怪物精灵子类;
2),动作组装器(帧动画封装);
3),方向计算器(根据不同点位移计算精灵朝向);
4),效果动作组装器(移动,旋转,放大缩小,赛贝尔曲线等动作组装);
5),精灵BUF,光环,状态控制器(精灵BUF状态,脚下光环,纹理状态控制);
6),精灵触摸区域;
7),精灵血条控制器;

3,特效系统;
1),普通特效;
2),技能特效;

2,精灵模型(Model层);
3,侠客阵列;
4,怪物阵列;

5,九宫格精灵连线系统(9Role-Connection);
1),九宫格连线系统(根据颜色相同补位连接算法);
2),引导连接(黑线引导连接及取消连接的算法,还包含夹角计算,位移更新弧度等算法);
3),连接取消(已连接阵法按原路线取消已连接的算法);
4),精灵阵法光环颜色(为精灵脚底添加颜色阵法)

6,精灵普通攻击;
1),精灵移动(动作);
2),普通攻击
3),精灵攻击&怪物被攻击&技能攻击特效&抖动
4),侠客精灵移动返回,怪物恢复站立;

7,补阵系统(12个精灵的补阵条件:1,不重复,2,按照从左到右,再从上到下规律补阵)
1),补移位算法(补数据模型model,补精灵视图view);
2),补齐后重置算法(重置当前序列TAG,注此处我是根据精灵坐标来重置当前阵法的序列);

以上一个攻击循环+补阵就是一个可以循环跑起来的单机版战斗框架;

二,战斗接模型正式数据后,普通攻击模块;
1,关卡信息展示;

2,地图切换,角色跑动等组合战斗场景特效;

3,精灵模型数据正式化;
0),玩家9个模型,好友/游侠(3个模型)。
1),侠客攻击,血量,恢复;
2),侠客属性(金木水火土,恢复);
3),怪物模型:攻击,血量,回合数,弱点属性;
4),精灵血条控制器;

4,侠客精灵普通攻击(细化)
1),单个精灵攻击:
侠客(移动,攻击,跑回)
怪物(被攻击,攻击特效,数值特效,连击特效,恢复站立)
2),多个精灵攻击,处理连招序列节奏;

5,回合定时控制器;(处理每个回合“玩家主动连线攻击一次/1回合”后检测当前战斗场景的回合状态,和所有精灵状态):
1),视图检测;
2),精灵检测;
3),怪物死亡检测;

6,怪物精灵攻击侠客:
1),根据回合控制器检测怪物0回合时进行攻击;
2),怪物攻击序列处理;
3),单个怪物攻击序列处理:
怪物随机选定攻击目标;
怪物攻击状态;
侠客精灵被攻击,被攻击特效,玩家扣血效果,扣血数值特效;
精灵恢复常态;
怪物攻击完更新回合数;

7,精灵补阵时根据策划数值需求添加必中BUFF几率;

8,玩家侠客总血量条(12个精灵血量之和);

9,怪物精灵死亡:
1),死亡检测;
2),死亡特效;

10,怪物攻击时,判定怪物技能的攻击侠客数目:
1),攻击单个;
2),攻击多个;

三,普通招式攻击模块

1,数据模型侠客招式数据获取;

1,侠客精灵怒气触发;

2,侠客精灵怒气效果制作;

3,在精灵普通攻击基础上重构招式攻击:
1),精灵变红色;
2),精灵位移;
3),位移时制作残影效果,渐隐效果;
5),假碰撞制作(节奏控制);
6),怪物被攻击效果,连击特效,数值连击特效(多次嘣数值),怪物扣血效果;
7),侠客,怪物恢复

4,侠客精灵普通攻击时按几率触发普通招式;

四,九连击大招攻击模块
1,9连构成条件

2,9连攻击:
1),先进行就连判定
2),判定成功,前8位侠客精灵执行原有BUFF及攻击操作(涵盖普通招式,普通攻击)
3),最后一位精灵为必杀技技能释放:
屏幕放大,
最后一位角色精灵超大CG闪现,
最后一位精灵原地释放全屏大招,
全屏怪物进行扣血操作。
4),所有精灵恢复

五,怪物加护盾模块
1,怪物普通攻击的同时增加护盾
2,护盾增加条件查询
3,怪物攻击加护盾:
0),选定目标个数,随机选定目标;
1),镜头跟随怪物放大效果;
2),怪物原地攻击;
3),怪物播放技能招式;
4),精灵侠客原地被攻击,玩家扣血效果,显示数字;
5),查询怪物增加护盾数,护盾类型;
6),增加护盾
4,破解护盾
0),破解护盾条件;
1),达成破解护盾的侠客攻击,将移除更新怪物护盾;
2),怪物护盾更新;

六,玩家死亡
1,玩家死亡判定;
2,死亡后弹出接关提示(是否接关?);
3,不接关,显示退出战斗提示;
4,接关,接关提示框消失,消耗复活石,进行接关:
购买复活石弹框,使用复活石
5,复活后,战斗回合为1;

七,关卡信息保存
1,打完每波怪物后进行地图切换
2,切换时显示关卡进度信息,如(2/3波);
3,切换关卡是通讯服务器进行数据保存,告诉服务器这波的情况;

八,战斗退出
1,弹出战斗退出框;
2,确定取消操作;
3,显示战斗失败弹框;
4,loading处理数据清理之后,进行对应跳转;

九,战斗总结算(该关卡,所有波次打完,进行总结算)
1,请求服务器,申请结算数据;
2,给服务器发送该关卡信息;
3,拿到服务器数据,解析;
4,显示该关卡的掉落,掉落什么东西,更新本地数据;
5,显示战斗胜利
6,loading处理数据清理之后,进行对应跳转;

分类: 未分类 标签:

Unable to parse plist in Cocos2dx 3.0

2014年8月22日 没有评论

Unable to parse plist in Cocos2dx 3.0

I want to parse this plist structure…Please help

What I am using right now is

mainDictionary = __Dictionary::createWithContentsOfFile(plistPath.c_str());
__Dictionary *exit = (__Dictionary*)__Dictionary::createWithDictionary(
            (__Dictionary*)mainDictionary->objectForKey(std::string("otherButton")));

But there are keys missing exit dictioanry…

In 3.0+ version the data structure like CCDictionary CCArray are deprecated. You have to use ValueMap or ValueVector etc
You can find the data structure under the folder ValueVector > CCValue.h

Here’s the code for reading this plist:

cocos2d::ValueMap gameData;
gameData = FileUtils::getInstance()->getValueMapFromFile("data.plist");

In case you want to read the values:

//1
std::string backgroundValue = gameData.at("background");

//2
ValueMap otherButtonsMap = gameData.at("otherButton").asValueMap();
std::string tagValue = otherButtonsMap.at("Tag");

//3
ValueVector buttonsVector = gameData.at("Buttons").asValueVector();
ValueMap item0 = buttonsVector.at(0).asValueMap();

Where the gameData is ValueMap (or in other words Dictionary or Map or HashMap)

Edit:

The answer by “Wez Sie Tato” is correct but in your case you should read the plist in ValueMap instead of ValueVector, because your plist is actually the Dictionary(ValueMap) and not the Array(ValueVector).

There is simply no key named “ExitButton” in the root of the plist (and thus in mainDictionary).

The mainDictionary keys will be “background”, “otherButton” and “Buttons”, each returning another Dictionary or Array instance.

If the “ExitButton” key is somewhere in the Buttons array, you have to enumerate the Buttons array and find the “ExitButton” key in one of the array items.

Try use ValueMap and FileUtils to load PList file. The way you try to load dictionary is from cocos2d-x v2.x. In cocos2d-x you should do something like this:

ValueMap mainDictionary = FileUtils::getInstance()->getValueMapFromFile(plistPath.c_str());

ValueMap exit = mainDictionary["otherButton"].asValueMap();
分类: cocos2d, stackoverflow精选 标签:

HTML5物理游戏开发 – 越野山地自行车(三)粉碎自行车

2014年8月19日 没有评论

自上一章发布到现在已时隔四月,实在对不住大家,让大家久等了~话说不是我不关注我的博客,而是事情一多起来写博客的时间就少了。待到今日有空了,回头看了看自己以前写的文章,猛得发现已经四个月不曾写文章了,便只得叫声:“苦也~”,我害怕本系列文章会拖得更久,于是立刻提笔,也好为本系列文章留个凤尾。

首先,大家来温习一下前面两篇里的内容吧:

HTML5物理游戏开发 – 越野山地自行车(二)创建一辆可操控的自行车

http://blog.csdn.net/yorhomwang/article/details/21300253

HTML5物理游戏开发 – 越野山地自行车(一)建立各式各样的地形

http://blog.csdn.net/yorhomwang/article/details/19710537

今天我们要实现的内容就是——当我们自行车的关键部分碰壁后,立刻使其粉碎,通俗点讲就是“自行车碎一地”。Ok,闲话何苦多说呢,我们就开始咯~

先放上两张截图吧:

HTML5物理游戏开发 - 越野山地自行车(三)粉碎自行车

HTML5物理游戏开发 - 越野山地自行车(三)粉碎自行车

※再次声明,本次开发用到了lufylegend.js开源html5游戏引擎和box2dweb物理引擎,请到官方网站下载使用。官方网站地址已在第一章中说过了。

一,粉碎原理

用过锤子的人都知道(如果你没用过,而且也不知道怎么用,建议你去问问雷神索尔),要砸碎一个自行车该怎么砸呢?如果你不会,我教你三招吧:

法一:使劲砸;这种方法适用于你想换把锤子

法二:到阿斯嘉找雷神大哥去,这个速率最快,估计不到抽完一根烟的工夫,你的自行车就只剩原子了

法三:去某个地方把锤子换成螺丝刀等工具,然后把你那自行车零件一块一块地给卸下来

显然,这三种方法各有所长,不过既然我们的自行车是一块一块地拼起来的,那么还是一块一块地给拆了好,于是,我选择了3(实际上是因为Box2dWeb没有锤子这玩意,也认不得雷神)。我们在上一章中提到过如何把零件拼起来,原理是运用了Box2dWeb里的关节,这些关节把零件们连在了一起,那么如果这些关节一销毁,那么这些零件就会散落。但是如何销毁关节呢?Box2dWeb的b2World里有一个DestroyJoint函数,参数就是你要销毁的b2Joint对象。我们来看看在lufylegend里如何销毁关节吧。

首先,在lufylegend里通过LBox2d关键关节的函数都会返回创建出的b2Joint对象,也就是说:

var j = LStage.box2d.setRevoluteJoint(a.box2dBody, b.box2dBody);
LStage.box2d.world.DestroyJoint(j);

我们可以把调用setRevoluteJoint创建出的旋转关节保存在变量j里,然后销毁时就直接调用LStage.box2d.world.DestroyJoint函数,参数则是j。

如果我们有多个关节怎么办呢?放数组里呗,想必聪明的你在我说这话之前就已经想到了这点吧。
原理搞定,那么我们开始看代码吧。

二,更新自行车类和Main类

上一章中我们着重讲了如何实现自行车类,这次因为要粉碎它,所以要先在它身上动手术,安放几个炸弹再说。装炸弹前要做好准备,以免把自己给炸over了,所以得先搞个盒子把要炸毁的地方装起来。恰巧Main类路过(关于Main请看第一章),我把它一手提了过来,唱声喏,道:“阁下暂且替我装两个东西,如何?”,Main类寻思道:“这厮可以掌管所有程序,万一这厮火了,一把delete把我等删个干净却不是个好事。”,于是他无可奈何地替我担负了这个重任。却说看官欲知是哪两个东西呢?原来是一个名叫jointList的数组和一个唤作gameOverController的bool型变量;这两厮一个管装所有生成的关节,一个管游戏是否结束。于是Main的构造器改造后如下:

function Main(){
	var s = this;
	base(s,LSprite,[]);

	/**设置场景大小*/
	s.sceneWidth = 8500;
	s.sceneHeight = LStage.height+1000;

	/**关节列表*/
	s.jointList = new Array();
	/**游戏结束控制器*/
	s.gameOverController = false;
}

却说上面那段明显有些水浒的风格,不好意思,近期《水浒传》看多了,望各位包含包含。

我们知道,炸弹都是要有点火线的,或者更高级一点的开关啊,反正就是一个引爆装置,这个工作还是交给Main吧。修改Main的init方法:

Main.prototype.init = function(){
	var s = this;

	/**加入边框*/
	s.addBorder();
	/**加入路面*/
	s.addRoad();
	/**加入自行车*/
	s.addBicycle();
	/**加入刚体碰撞事件*/
	LStage.box2d.setEvent(LEvent.POST_SOLVE,s.postSolve);
	/**加入循环事件*/
	s.addEventListener(LEvent.ENTER_FRAME,s.loop);
};

主要是加了个调用LStage.box2d.setEvent函数。这个函数是LBox2d类的一个方法(LStage.box2d是LBox2d的实例化对象),具体的使用方法是这样子滴:

■setEvent(type, func)

参数介绍:

type box2d世界里的碰撞事件类型

func 触发事件时调度的函数

碰撞事件的类型可以为:

LEvent.BEGIN_CONTACT:刚刚碰撞开始的时候会触发这个函数
LEvent.END_CONTACT:碰撞结束的时候会触发这个函数
LEvent.POST_SOLVE:碰撞后会处理这个函数
LEvent.PRE_SOLVE:碰撞前即将碰撞的时候

这里我们选的是POST_SOLVE,也就是碰撞后会处理这个函数,其实选择其他的事件类型,效果也应该是一样的。事件触发时调度的函数是postSolve,这个函数也交给Main吧~ [Main类:说好的装两个呢(T_T)]

Main.prototype.postSolve = function(contact){
	if(world.gameOverController)return;
	var l = world.jointList;
	if(l.length == 0)return;
	//获取碰撞的LSprite对象
	var cA = contact.GetFixtureA().GetBody().GetUserData();
	var cB = contact.GetFixtureB().GetBody().GetUserData();
	//判断是否摧毁自行车
	if(
		//--------------------------------------------
		//条件一:当自行车和墙碰撞时
		//--------------------------------------------
		(
			(cA.name=="wall" && cB.name=="bicycle")
			||
			(cA.name=="bicycle" && cB.name=="wall")
		)
		||
		//--------------------------------------------
		//条件二:当自行车的车把、车把到轮子的支架或者车座碰到其他物体时
		//--------------------------------------------
		(
			(cA.trigger=="destroy_bicycle" && cB.name!="bicycle")
			||
			(cA.name!="bicycle" && cB.trigger=="destroy_bicycle")
		)
	){
		//去掉自行车上的所有关节以达到催毁自行车
		for(var i in l){
			var jo = l[i];
			//去掉关节
			LStage.box2d.world.DestroyJoint(jo);
			//将游戏结束控制器设置为游戏结束
			world.gameOverController = true;
		}
		//从自行车关节列表中移除所有关节
		l.length = 0;

		//添加游戏结束提示
		var gameOverText = new LTextField();
		gameOverText.text = "Game Over";
		gameOverText.size = 50;
		gameOverText.alpha = 0;
		gameOverText.x = (LStage.width-gameOverText.getWidth())*0.5;
		gameOverText.y = (LStage.height-gameOverText.getHeight())*0.5;
		addChild(gameOverText);
		LTweenLite.to(gameOverText,5,{
			delay:1.5,
			alpha:1
		});
	}
};

该函数会接受一个参数,参数是个啥对象呢?其实我也不清楚,反正里面有GetFixtureA和GetFixtureB这两个函数。这两个函数能取到正在碰撞的刚体的刚形,通过刚形的GetBody取到b2Body对象,然后用b2Body的GetUserData取到最终的LSprite对象。

参数介绍完了,我还是来介绍一下这个函数的执行逻辑吧。首先,如果游戏已经结束或者jointList为空的数组,则不再运行后面的代码;如果继续运行代码,则首先把碰撞的两个刚体所在的LSprite取出来,然后进行游戏结束判断,如果通过则执行游戏结束的代码。有人也许纳闷那个world是个啥?如果你从第一章看起,就应该会明白了。主要是postSolve是个回调函数,里面的this不是指向Main的。

在那个长达19行的判断条件里,我设定了两个条件,满足这两个条件之一,便进行销毁关节:条件一:当自行车和墙碰撞时;条件二:当自行车的车把、车把到轮子的支架或者车座碰到其他物体时。

销毁部分的代码主要是注意,在box2d里,销毁关节用DestroyJoint这个函数,这一点在“粉碎原理”中就已经提到过了,这个函数是在b2World类中的,LBox2d的world属性就是b2World的实例化对象。

能不能结束游戏关键要看碰撞的b2Body所在的LSprite对象的name和trigger(英文翻译过来是“触发器”的意思),这些属性在哪里设置的呢?当然是在构造自行车的类Bicycle类里呢。接下来就来看看Bicycle类里加入&改进的代码。

首先来看Bicycle的init函数的变化:

Bicycle.prototype.init = function(){
	var s = this;

	var sx = s.sx;
	var sy = s.sy; 

	/**轮子半径*/
	var wheelR = 20;
	/**轮子之间的距离*/
	var gapBetweenWheelAndWheel = 100;
	/**车手柄到轮子的距离*/
	var gapBetweenWheelAndHandlebar = 50;
	/**车把尺寸*/
	var handlebarWidth=20,handlebarHeight=5;
	/**座椅到轮子支架的距离*/
	var gapBetweenWheelFrameAndSeat = 30;
	/**座椅尺寸*/
	var seatWidth=30,seatHeight=5;
	/**支架尺寸*/
	var frameSize = 10;

	/**加入支架*/
	//轮子上的支架
	var frameAObj = new LSprite();
	frameAObj.x = sx+gapBetweenWheelAndWheel/2;
	frameAObj.y = sy+frameSize/2;
	frameAObj.addBodyPolygon(gapBetweenWheelAndWheel,frameSize,1,5);
	world.addChild(frameAObj);
	s.bodyList.push(frameAObj);
	//车把到轮子的支架
	var frameBObj = new LSprite();
	frameBObj.trigger = "destroy_bicycle";
	frameBObj.x = sx+gapBetweenWheelAndWheel-frameSize/2;
	frameBObj.y = sy-gapBetweenWheelAndHandlebar/2;
	frameBObj.addBodyPolygon(frameSize,gapBetweenWheelAndHandlebar,1,2);
	world.addChild(frameBObj);
	s.bodyList.push(frameBObj);

	/**加入车把*/
	var handlebarObj = new LSprite();
	handlebarObj.trigger = "destroy_bicycle";
	handlebarObj.x = sx+gapBetweenWheelAndWheel-handlebarWidth/2-frameSize;
	handlebarObj.y = sy-gapBetweenWheelAndHandlebar+handlebarHeight/2;
	handlebarObj.addBodyPolygon(handlebarWidth,handlebarHeight,1,.5);
	world.addChild(handlebarObj);
	s.bodyList.push(handlebarObj);

	/**加入座椅*/
	//座椅到轮子支架的支架
	var seatFrameObj = new LSprite();
	seatFrameObj.x = sx+30;
	seatFrameObj.y = sy-gapBetweenWheelFrameAndSeat/2;
	seatFrameObj.addBodyPolygon(frameSize,gapBetweenWheelFrameAndSeat,1,1);
	world.addChild(seatFrameObj);
	s.bodyList.push(seatFrameObj);
	//座椅
	var seatObj = new LSprite();
	seatObj.trigger = "destroy_bicycle";
	seatObj.x = sx+30;
	seatObj.y = sy-gapBetweenWheelFrameAndSeat-seatHeight/2;
	seatObj.addBodyPolygon(seatWidth,seatHeight,1,.5);
	world.addChild(seatObj);
	s.bodyList.push(seatObj);

	/**加入轮子*/
	//左边轮子A
	var wheelAObj = new LSprite();
	wheelAObj.x = sx-wheelR;
	wheelAObj.y = sy;
	wheelAObj.addBodyCircle(wheelR,wheelR,wheelR,1,2.5,.2,.4);
	world.addChild(wheelAObj);
	s.bodyList.push(wheelAObj);
	//右边轮子B
	var wheelBObj = new LSprite();
	wheelBObj.x = sx+gapBetweenWheelAndWheel-wheelR;
	wheelBObj.y = sy;
	wheelBObj.addBodyCircle(wheelR,wheelR,wheelR,1,2.5,.2,.4);
	world.addChild(wheelBObj);
	s.bodyList.push(wheelBObj);
	
	/**添加关节*/
	//轮子A和轮子支架的旋转关节
	world.jointList.push(LStage.box2d.setRevoluteJoint(frameAObj.box2dBody, wheelAObj.box2dBody));
	//轮子B和轮子支架的旋转关节
	world.jointList.push(LStage.box2d.setRevoluteJoint(frameAObj.box2dBody, wheelBObj.box2dBody));
	//车把到轮子的支架和轮子支架的焊接关节
	world.jointList.push(LStage.box2d.setWeldJoint(frameAObj.box2dBody, frameBObj.box2dBody));
	//车把到轮子的支架和车把的焊接关节
	world.jointList.push(LStage.box2d.setWeldJoint(handlebarObj.box2dBody, frameBObj.box2dBody));
	//轮子的支架和座椅的焊接关节
	world.jointList.push(LStage.box2d.setWeldJoint(seatFrameObj.box2dBody, frameAObj.box2dBody));
	//座椅的支架和座椅的焊接关节
	world.jointList.push(LStage.box2d.setWeldJoint(seatFrameObj.box2dBody, seatObj.box2dBody));
	
	/**遍历所有自行车零件刚体*/
	for(var key in s.bodyList){
		var obj = s.bodyList[key];
		//加入鼠标拖动
		if(obj.box2dBody)obj.setBodyMouseJoint(true);
		//设置对象名称
		obj.name = "bicycle";
	}

	/**设置主刚体*/
	s.mainBody = frameAObj.box2dBody;
	/**设置拉压操作刚体*/
	s.tcBody = wheelBObj.box2dBody;
};

主要改的是这里:

/**添加关节*/
//轮子A和轮子支架的旋转关节
world.jointList.push(LStage.box2d.setRevoluteJoint(frameAObj.box2dBody, wheelAObj.box2dBody));
//轮子B和轮子支架的旋转关节
world.jointList.push(LStage.box2d.setRevoluteJoint(frameAObj.box2dBody, wheelBObj.box2dBody));
//车把到轮子的支架和轮子支架的焊接关节
world.jointList.push(LStage.box2d.setWeldJoint(frameAObj.box2dBody, frameBObj.box2dBody));
//车把到轮子的支架和车把的焊接关节
world.jointList.push(LStage.box2d.setWeldJoint(handlebarObj.box2dBody, frameBObj.box2dBody));
//轮子的支架和座椅的焊接关节
world.jointList.push(LStage.box2d.setWeldJoint(seatFrameObj.box2dBody, frameAObj.box2dBody));
//座椅的支架和座椅的焊接关节
world.jointList.push(LStage.box2d.setWeldJoint(seatFrameObj.box2dBody, seatObj.box2dBody));

我把所有的关节都加入到wolrd的jointList里了,这样一来我们就可以通过遍历取出关节来,然后进行销毁,这一点在Main的postSolve里就已经实现了。

还有修改的就是:1,给所有的属于自行车的刚体都加了一个name属性,设定为:“bicycle”;2,给关键部位的刚体(车把、车把到轮子的支架和车座)加了trigger属性,设置为“destroy_bicycle”,表示如果这些部位碰到了其他不属于自行车的刚体,就结束游戏。

至于name为"wall"的刚体其实就只有一个(bottomBorder底部边框,促使自行车跌到底部时结束游戏):

Main.prototype.addBorder = function(){
	var s = this;

	/**创建边框*/
	//设置边框尺寸
	var borderSize = 10;
	//顶部边框
	var topBorder = new LSprite();
	topBorder.x = s.sceneWidth/2;
	topBorder.y = 5;
	topBorder.addBodyPolygon(s.sceneWidth,borderSize,0);
	s.addChild(topBorder);
	//右部边框
	var rightBorder = new LSprite();
	rightBorder.x = s.sceneWidth-5;
	rightBorder.y = s.sceneHeight/2;
	rightBorder.addBodyPolygon(borderSize,s.sceneHeight,0);
	s.addChild(rightBorder);
	//底部边框
	var bottomBorder = new LSprite();
	bottomBorder.name = "wall";
	bottomBorder.x = s.sceneWidth/2;
	bottomBorder.y = s.sceneHeight-5;
	bottomBorder.addBodyPolygon(s.sceneWidth,borderSize,0);
	s.addChild(bottomBorder);
	//左部边框
	var leftBorder = new LSprite();
	leftBorder.x = 5;
	leftBorder.y = s.sceneHeight/2;
	leftBorder.addBodyPolygon(borderSize,s.sceneHeight,0);
	s.addChild(leftBorder);
};

Ok,运行代码,得到的就是本文最上方图片所示的效果了。

奉上源代码下载地址:http://files.cnblogs.com/yorhom/box2dBicycle%283%29.rar

测试地址:http://game.hdc.h5stars.com/201428053f36b0853f15~

本系列教程就到此为止了,其实如果要做一个真正的“越野山地自行车”这种游戏,还需要对刚体进行贴图,胜利判断等。这些都很简单,大家可以自己动手做一做吧~(目前我做的demo应该可以当作一种发泄工具吧,压力大了,就来把这辆虚拟的自行车拿来狠狠地摔吧~ 哈哈)

还有同学(这会儿可是真正意义上的同学了…)问我如何操作,好吧,算我失误,没有在本章告诉大家(其实你看了第两章就会知道的),在这里补充一下。操作说明:上下左右键操作,至于这些按键对应的效果就自己摸索吧,实在总结不出来就去第二章慢慢找吧,我也就偷个懒吧~

本章就先到这里了。如果文章有任何疏漏之处,欢迎指正。当然,有不懂之处也欢迎各位在本文下方留言,我会尽力回复大家的。

—————————————————————-

欢迎大家转载我的文章。

转载请注明:转自Yorhom’s Game Box

http://blog.csdn.net/yorhomwang

欢迎继续关注我的博客

分类: 未分类 标签:

【cocos2d-x 手游研发小技巧(8)通讯的数据压缩与解压 】

2014年8月8日 没有评论

天说一下手机游戏通讯协议中的数据问题,大量的数据将给服务器端和客户端带来很大的压力,一般来说。

转载请注明出处:http://www.cnblogs.com/zisou/p/cocos2dxJQ-8.html

游戏的数据分静态数据和服务器传过来的动态数据,静态数据大部分通过lua,xml,csv等格式来存储使用。

动态数据则由服务器发给我们了,不管是用弱联网还是强联网,往往服务器会发送大量的一些数据给客户端接收,

如果思路设计的不好,往往发送接受而不止一次,那么遇到这样的服务器客户端交互的大数据问题只能靠压缩和解压来处理了,

压缩后不但可以将大数据变小,还可以为用户节省流量,一举多得的事情是非常赞的。

最近我的一个项目就使用了这个压缩和解压工具,源码是C写的,所以可以放心的跨平台使用。

 

下面我说一下具体使用的细节:

 

1,在使用之前需要先初始化一下,且初始化一次就好,放在构造函数中

 

     //消息压缩加密
        if (lzo_init() != LZO_E_OK)
        {
            printf("internal error - lzo_init() failed !!!/n");
            printf("(this usually indicates a compiler bug - try recompiling/nwithout optimizations, and enable '-DLZO_DEBUG' for diagnostics)/n");
        }

 

2,在给服务器发送的时候也可以压缩,压缩方法如下:

    lzo_uint in_len;
    lzo_uint out_len;
    lzo_bytep in_last = (lzo_bytep)json_data;
    in_len = strlen(json_data);
    if (lzo1x_1_compress(in_last,in_len,outlast,&out_len,wrkmem) == LZO_E_OK)
    {
        printf("compressed %lu bytes into %lu bytes/n",
               (unsigned long) in_len, (unsigned long) out_len);
    }
    else
    {
        CCLOG("错误了!");
        return 0;
    }

3,接收到服务器数据的时候进行解压,当然在这时,接受的到包是压缩后的包,而且直接接收到的压缩包直接输出是看不到值的,但包的大小

是可以通过服务器获取,所以解包的时候和上面是相反的;

                    lzo_uint old_len;
                    lzo_uint new_len;
                    
                    char *strbuff_cc = new char[REC_BUFSIZE];
                    memset(strbuff_cc, 0, REC_BUFSIZE);
                    old_len = allsize;
                    
                    if (lzo1x_decompress((lzo_bytep)buffer_all,allsize,(lzo_bytep)strbuff_cc,&new_len,NULL) == LZO_E_OK)
                    {
                        printf("decompressed %lu bytes back into %lu bytes/n",
                               (unsigned long) new_len, (unsigned long) old_len);
                    }
                    else
                    {
                        CCLOG("解压err!");
                    }
                    
                    strbuff = string(strbuff_cc);
                    
                    if(strbuff.length()>0)
                    {
                        SocketThread::paserBody(strbuff);
                        int num = strlen(strbuff.c_str());
                        CCLOG("all package >>>>>%s,and size:%d",strbuff.c_str(),num);
                        CCLOG("package size:%d",num);
                        
                    }

 

Ok,我们来看一些效果,将使你会大吃一惊的!

【cocos2d-x 手游研发小技巧(8)通讯的数据压缩与解压 】

已将包大小为16567 的解压出来为123768 ,12K的东西压缩为1K,通讯质量以及速度将会快很多,以及流量都会节省不少。

 

我将这个压缩工具分享一下:

百度云盘下载

 

分类: 未分类 标签:

iOS平台添加Google Admob -2/2(Unity3D开发之八)

2014年8月7日 没有评论

猴子原创,欢迎转载。转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢!

原文地址: http://www.cocos2dev.com/?p=572

iOS平台添加Google Admob -2/2(Unity3D开发之八)

上一篇文章中主要是编写了iOS Admob的接口实现。那么现在我们要做的事就是在unity中调用iOS Admob并展示。

一、实现Unity中对外接口,内部负责调用iOS Admob接口。
LHiOSAdmob.cs

using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
using System.Linq;
using System.Collections.Generic;

public enum GADAdSize:int
{
	// iPhone and iPod Touch ad size. Typically 320x50.
	kGADAdSizeBanner = 1,

	// Taller version of kGADAdSizeBanner. Typically 320x100.
	kGADAdSizeLargeBanner,

	// Medium Rectangle size for the iPad (especially in a UISplitView's left pane). Typically 300x250.
	kGADAdSizeMediumRectangle,

	// Full Banner size for the iPad (especially in a UIPopoverController or in
	// UIModalPresentationFormSheet). Typically 468x60.
	kGADAdSizeFullBanner,

	// Leaderboard size for the iPad. Typically 728x90.
	kGADAdSizeLeaderboard,
	
	// Skyscraper size for the iPad. Mediation only. AdMob/Google does not offer this size. Typically
	// 120x600.
	kGADAdSizeSkyscraper,
	
	// An ad size that spans the full width of the application in portrait orientation. The height is
	// typically 50 pixels on an iPhone/iPod UI, and 90 pixels tall on an iPad UI.
	kGADAdSizeSmartBannerPortrait,
	
	// An ad size that spans the full width of the application in landscape orientation. The height is
	// typically 32 pixels on an iPhone/iPod UI, and 90 pixels tall on an iPad UI.
	kGADAdSizeSmartBannerLandscape
}

public class LHiOSAdmob : MonoBehaviour {
	[DllImport("__Internal")]
		private static extern void startRequestAdmob(string admobId, int adSizeId, float pixelX, float pixelY);
	[DllImport("__Internal")]
	private static extern void setAdmobHidden(bool isHidden);

	public static LHiOSAdmob Instance;
	private GADAdSize adSize;

	void Awake()
	{
		// singleton
		if (Instance != null)
		{
			Debug.LogError("Multiple instances of LHiOSAdmob");
		}
		Instance = this;
	}
	
	// Init google admob
	// It will request the admob after five seconds.
	// It's will auto show admob.
	public void InitAdmob(string admobId, GADAdSize size, Vector2 pos)
	{
		#if UNITY_IPHONE
		int adSizeId = (int)size;
		startRequestAdmob(admobId, adSizeId, pos.x, pos.y);
		#else
		Debug.Log("Admob only run on iPhone platform");
		#endif
	}

	// Set Admob BannerView is visible or not
	public void SetAdmobVisible(bool isVisible)
	{
		#if UNITY_IPHONE
		setAdmobHidden(!isVisible);
		#else
		Debug.Log("Admob only run on iPhone platform");
		#endif
	}

	// Use this for initialization
	void Start () {

	}
	
	// Update is called once per frame
	void Update () {
		
	}
}

这是一个单例接口类,内部负责调用iOS Admob接口,并开放对外接口。供其他c#文件调用。
注意:你可以制作一个Prefab,并包含该脚本。将该Prefab拖放到场景中。在需要显示admob的地方调用。

二、测试admob显示,也就是在你需要的地方。

TestAdmob.cs

using UnityEngine;
using System.Collections;

public class TestAdmob : MonoBehaviour {
	// Use this for initialization
	void Start () {
		LHiOSAdmob.Instance.InitAdmob("your admob id", GADAdSize.kGADAdSizeSmartBannerLandscape, new Vector2(0, 150));
	}
	
	// Update is called once per frame
	void Update () {
	
	}
}

ok, 现在可以Build iOS工程了。

注意:

Build的iOS工程,要添加对应的编译选项和iOS框架。

1、Other Linker Flags 添加 -ObjC

2、Xcode->Targets->Build Phases->Link Binary With Libraries 添加以下框架:
AdSupport
AudioToolbox
AVFoundation
CoreGraphics
MessageUI
StoreKit
SystemConfiguration

分类: 未分类 标签:

iOS平台添加Google Admob -1/2(Unity3D开发之七)

2014年8月7日 没有评论

猴子原创,欢迎转载。转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢!

原文地址: http://www.cocos2dev.com/?p=567

iOS平台添加Google Admob -1/2(Unity3D开发之七)

Unity调用iOS还是非常简单的,晚上空闲时间写了unity在IOS平台加载Google Admob。需要的朋友可以看下。

一、首先编写Admob的iOS的代码。
请去https://apps.admob.com下载iOS Admob SDK, 并注册app id。如果这一步不熟悉,请自行Google,网上应该很多。

1、IOSAdmob类,主要实现Admob的创建和加载
IOSAdmob.h

//
//  IOSAdmob.h
//  IOSAdmob
//
//  Created by LiuYanghui on 14-8-4.
//  Copyright (c) 2014年 LiuYanghui. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "GADBannerView.h"
#import "GADBannerViewDelegate.h"

@interface IOSAdmob : NSObject<GADBannerViewDelegate>

- (void)startRequestAdmob:(NSString*)admobId AdSize:(GADAdSize)size Position:(CGPoint)pos;
- (void)setAdmobHidden:(BOOL)isHidden;

@end

IOSAdmob.m

//
//  IOSAdmob.m
//  IOSAdmob
//
//  Created by LiuYanghui on 14-8-4.
//  Copyright (c) 2014年 LiuYanghui. All rights reserved.
//

#import "IOSAdmob.h"

@interface IOSAdmob ()
    @property (nonatomic) BOOL isHidden;
    @property (nonatomic) CGRect adRect;
    @property (nonatomic, readonly, strong) UIViewController* rootViewController;
    @property (nonatomic, readonly, strong) GADBannerView* adBannerView;
@end

@implementation IOSAdmob

- (id) init
{
	self = [super init];
	if(self != nil)
	{
        _isHidden = NO;
        _adRect = CGRectZero;
        _rootViewController = [[[UIApplication sharedApplication] keyWindow] rootViewController];
        _adBannerView = nil;
	}
	return self;
}

- (void) dealloc
{
    [_rootViewController release];
    [_adBannerView release];
    
	[super dealloc];
}

- (void) startRequestAdmob:(NSString*)admobId AdSize:(GADAdSize)size Position:(CGPoint)pos
{
    if (_adBannerView) { return; }
    
    _adBannerView = [[GADBannerView alloc] initWithAdSize:size];
//    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
//        
//    }else {
//
//    }
    
    _adRect = CGRectMake(pos.x, pos.y, _adBannerView.frame.size.width, _adBannerView.frame.size.height);
    _adBannerView.adUnitID = admobId;
    _adBannerView.delegate = self;
    [_adBannerView setRootViewController:_rootViewController];
    _adBannerView.frame = _adRect;
    [_rootViewController.view addSubview:_adBannerView];
    
    double delayInSeconds = 5.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        NSLog(@"Retrying to load request");
        [_adBannerView loadRequest:[self createRequest]];
    });
}

- (void)setAdmobHidden:(BOOL)isHidden
{
    _isHidden = isHidden;
    if (_adBannerView) {
        [_adBannerView setHidden:_isHidden];
    }
    //        [UIView animateWithDuration:0.5 animations:^ {
    //            _adBannerView.frame = _adRect;
    //        }];
}

#pragma mark GADRequest generation

// Here we're creating a simple GADRequest and whitelisting the simulator
// and two devices for test ads. You should request test ads during development
// to avoid generating invalid impressions and clicks.
- (GADRequest *)createRequest {
    GADRequest *request = [GADRequest request];
    return request;
}

#pragma mark GADBannerViewDelegate impl

// Since we've received an ad, let's go ahead and set the frame to display it.
- (void)adViewDidReceiveAd:(GADBannerView *)adView {
    NSLog(@"Received ad");
    _adBannerView.frame = _adRect;
    [_adBannerView setHidden:_isHidden];
}

- (void)adView:(GADBannerView *)view
didFailToReceiveAdWithError:(GADRequestError *)error {
    NSLog(@"Failed to receive ad with error: %@", [error localizedFailureReason]);
    double delayInSeconds = 5.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        NSLog(@"Retrying to load request");
        [_adBannerView loadRequest:[self createRequest]];
    });
}

@end

二、Admob对外单例类接口
IOSAdmobManager.h

//
//  IOSAdmobManager.h
//  IOSAdmob
//
//  Created by LiuYanghui on 14-8-4.
//  Copyright (c) 2014年 LiuYanghui. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface IOSAdmobManager : NSObject

+ (IOSAdmobManager *)getInstance;

- (void)startRequestAdmob:(NSString*)admobId AdSizeId:(NSInteger)adSizeId Position:(CGPoint)pos;
- (void)setAdmobHidden:(BOOL)isHidden;

@end

IOSAdmobManager.m

//
//  IOSAdmobManager.m
//  IOSAdmob
//
//  Created by LiuYanghui on 14-8-4.
//  Copyright (c) 2014年 LiuYanghui. All rights reserved.
//

#import "IOSAdmobManager.h"
#import "IOSAdmob.h"

@interface IOSAdmobManager ()
@property (nonatomic, readonly, strong) IOSAdmob* admob;
@end


@implementation IOSAdmobManager

+ (IOSAdmobManager *)getInstance
{
    static IOSAdmobManager *mgr = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        mgr = [[self alloc] init];
    });
    return mgr;
}

- (id)init
{
    self = [super init];
    if (self) {
        _admob = [[IOSAdmob alloc] init];
        return self;
    }
    return nil;
}

- (void)startRequestAdmob:(NSString*)admobId AdSizeId:(NSInteger)adSizeId Position:(CGPoint)pos
{
    switch (adSizeId) {
        case 1:
            [_admob startRequestAdmob:admobId AdSize:kGADAdSizeBanner Position:pos];
            break;
            
        case 2:
            [_admob startRequestAdmob:admobId AdSize:kGADAdSizeLargeBanner Position:pos];
            break;
            
        case 3:
            [_admob startRequestAdmob:admobId AdSize:kGADAdSizeMediumRectangle Position:pos];
            break;
            
        case 4:
            [_admob startRequestAdmob:admobId AdSize:kGADAdSizeFullBanner Position:pos];
            break;
            
        case 5:
            [_admob startRequestAdmob:admobId AdSize:kGADAdSizeLeaderboard Position:pos];
            break;
            
        case 6:
            [_admob startRequestAdmob:admobId AdSize:kGADAdSizeSkyscraper Position:pos];
            break;
            
        case 7:
            [_admob startRequestAdmob:admobId AdSize:kGADAdSizeSmartBannerPortrait Position:pos];
            break;
            
        case 8:
            [_admob startRequestAdmob:admobId AdSize:kGADAdSizeSmartBannerLandscape Position:pos];
            break;
            
        default:
            [_admob startRequestAdmob:admobId AdSize:kGADAdSizeBanner Position:pos];
            break;
    }
}

- (void)setAdmobHidden:(BOOL)isHidden
{
    [_admob setAdmobHidden:isHidden];
}

@end

三、为第二步中的单例接口注册C函数接口,提供给Unity使用
LHAdmob.mm

//
//  LHAdmob.mm
//  IOSAdmob
//
//  Created by LiuYanghui on 14-8-4.
//  Copyright (c) 2014年 LiuYanghui. All rights reserved.
//

#import "IOSAdmobManager.h"

extern "C" {
    void startRequestAdmob(const char* admobId, int adSizeId, float pixelX, float pixelY)
    {
        [[IOSAdmobManager getInstance] startRequestAdmob:[NSString stringWithUTF8String:admobId] AdSizeId:adSizeId Position:CGPointMake(pixelX, pixelY)];
    }
    
    void setAdmobHidden(bool isHidden)
    {
        [[IOSAdmobManager getInstance] setAdmobHidden:isHidden];
    }
}

OK,到这里已经完成了Admob的创建和Unity接口编写。

注意:
1、Unity导出的iOS工程默认没有使用arc,所以以上代码全部未使用arc。
Xcode->Targets->Build Settings->Objective-C Automatic Reference Counting 设置NO,即关闭arc。

2、针对上诉接口创建什么类型的xCode工程 ?
你可以创建任意的Xcode工程,编写以上代码。也可以创建iOS->Framework & Library->Cocos Touch Static Library 静态库工程。

3、以上代码如何加到Unity?
第一个方法:直接将以上代码文件复制到Unity->Assets/Plugins/iOS 文件夹中,该文件是Unity指定的插件文件。
第二个方法:如果你创建的是静态库工程,Xcode->Product->Archive 编译出.a静态库文件,将.a文件复制到Unity->Assets/Plugins/iOS 文件夹中。

分类: 未分类 标签:

游戏中的心理学:心理画像之大R篇

2014年8月6日 没有评论
作为一个游戏人,每天都要不可避免得谈论我们的上帝——玩家,而谈论最多的,自然是玩家的付费行为。
 
在对付费行为的讨论分析过程中,业内逐渐产生了免费玩家、小R、中R、大R的分类方法并得到了共识。这些“社会角色”有哪些特点,又该如何伺候呢?笔者不才,希望与大家讨论分享一些不成熟的看法,希望大家批评指正。
 
(本文是基于统计学意义上的归纳论述,不针对任何个体,在阅读过程中不要纠结于你所熟悉的某个大R与本文描述不符,避免影响本文的阅读价值,具体可参阅文末《各种PS》的2、3条)
 
今天先说说大R。

大R的特点:
 
大R是游戏行业能够如此迅猛发展最大的推动力,正是他们塑造了无数游戏行业的创富神话并吸引无数“有志青年”义无反顾的投身其中。首先在此代表同行向衣食父母表示感谢,尽管游戏从业者和大R彼此内心深处都不太尊敬对方。
 
大R用户的存在,一定程度上“得益”于当前财富分配的现状,一小部分人分得了不可想象的财富,而大部分人所获寥寥。这其实也是为什么中美市场付费率差异很大的原因之一,中产阶级为主的社会结构和穷人为主的社会结构相比,前者的付费率显然会高得多。
 
话题稍微有些远,让我们继续为大R进行心理画像。有位游戏理论家曾经说过,游戏是最便宜的娱乐活动,我想这哥们肯定没怎么在免费场地上玩过足、篮、乒、羽,但是他说的也确实有道理,因为在游戏中为你服务的,主要是机器和代码,相比之下,其他娱乐业的场所、人工费用就显得非常昂贵了。而在游戏中神一样存在的大R们,在真正的富人圈里,其实大都中等偏下不受待见。根据“古典心理学”的分析——“宁为鸡口,毋为牛后”( 《战国策 韩策一》),大R玩游戏很大程度上是 “鸡口”在寻找自己的鸡身、鸡尾、鸡爪们。
 
游戏是一个非常消耗时间的娱乐方式,尤其是能够让大R有用武之地的游戏一般都有着比较复杂的养成体系,侧面说明了大R群体的空闲时间比较多。实际调查下来,相当一部分是煤老板(广义上说就是一切个体矿产业主)、包工头(建筑、道路、绿化、装修等等各种项目)、有权的公务员(显然是别人送的钱或者直接帮忙充到游戏里)、私营业主(服装、电器、食品、家具等等)。听上去就比较闲对不对?
 
大R的另外一个特点就是比较接地气,属于富人群体中不注重“贵族化”的一类,欣赏不了音乐会、芭蕾舞,好端端的高端社交集会搞成“海天盛筵”,看场马球基本都在看周边的妹子。
 
大R还有一个鲜明的特点就是不低调,富人圈里,低调和不低调差不多成了两个极端,而我们的大R,大部分都妥妥的站在不低调的这个极端上。(说句公道话,富人在统计学范畴上还真不一定比普通人嚣张,只是富人中不低调的由于钱的缘故嚣张起来能够被更多人看到和议论,所以才给人一个错觉,中国的富人太嚣张,太飞扬跋扈。)
 
根据马斯洛的需求层次理论分析,大R们主要希望满足的是尊重需要和自我实现需要。尊重需要在现实生活中已经得到了相当程度的满足,但现实生活接触到的人毕竟有限,而网游中动辄数万人(玩家感觉,实际单服几千人算多的了)的尊重与崇拜是现实社会中很难得到的。同样,在现实生活中,称霸天下、武功盖世甚至制霸宇宙都是不可能的,这些成只能在游戏中获得。
 
(上述所有描述都不包含游戏行业以及与游戏密切相关的传统和移动互联网行业的富豪们,他们玩游戏很大一部分原因是学习、了解、参考和比较,其次才是兴趣。在大R中虽然占有一定比例,却不是典型用户)
 
小结:
总结起来,我们的大R在富人圈里不太受待见,也不稀罕把大把的空闲时间用在那些所谓“贵族化”的无聊事情上;他们随着改革大潮而起,也大都保留着接地气的娱乐方式;他们做事高调,把游戏作为炫耀自己的阵地,并在这里获得被尊重与自我实现的心理满足。
 
服务策略:
首先我们必须感谢大R的存在,通过他们实现了一定程度上的财富再分配,给无权无钱的人们一条靠本事通往成功的路,在天朝这样的路少之又少。当然,与此相应的,这条路上挤满了人。
 
挤满人就意味着竞争激烈,具体表现在:
 
1、为大R提供服务的机会变少。
 
2、游戏中的大R被抢夺走的概率增加。
 
3、让大R得到满足越来越难。
 
增加服务机会
为什么服务机会变少呢?是因为游戏数量迅猛增加,大R被分流。那么在推广阶段如何获得更多的大R就成为了课题。
 
大R大多数并非骨灰玩家,他们浏览专业网站和各种排行榜的次数并不太多,他们绝不属于对新游戏“先知先觉”的群体。
 
虽然当今的互联网最讲究“口碑营销”,但如果你无法精确定位到“有朋友是大R”这个群体,也就无法利用他们通过更短的关系链到达大R用户。所以,在你没有更有效的“口碑营销”手段时,你到达大R用户的概率就不会高于竞争对手。那么你可以做三件事:一、想办法筛选出“有朋友是大R”的人群,二、想出更有效的“口碑营销”手段,三、采用传统广告模式去到达大R。
 
这三种方式都值得尝试,今天我们重点说说第三点,广告。采用地铁广告主攻免费用户和小R是一种战术,但不在今天讨论的范围内;写字楼广告绝对是败笔因为知本家和大R用户是两类互相鄙视的人;电视广告的受众和游戏用户重合度越来越低,成本根本hold不住。那该怎么整?大R们经常出没的地方:中高档住宅区、商业中心、中高档饭店、中高档娱乐场所。让我们在这些地方头脑风暴一下:1、住宅区的电梯广告很贵但值得一试。2、有兴趣订制点上档次带广告的一次性饭盒提供给周边提供外送的饭馆的吗?3、小区周边洗车房宝马车主下载游戏免费洗车(几个豪车品牌换着来,全搞的话洗车房会死得很惨)。4、商业中心购物满5000赠送等价值游戏装备(白富美们不会玩游戏,但是“免费的魔力”会让她们兴高采烈的拿给自己的男人)。5、如果设有老公寄存处,自然是兵家必争之地,在这里设置个连着大屏幕的展示机,在上边完成某种任务可以给女友赢得打折机会。6、饭店里边的广告位贵得离谱,但代驾这个渠道是否可以利用?7、娱乐场所的妹纸们如果用好了,推广效果会让你震惊。
 
防止挖墙脚
大R离去是做游戏最痛苦的事之一,该如何避免这一点呢?首先我们要保持畅通的沟通,比如设有专人客服。其次对大R有超过普通用户的统计记录,主要用于减轻回档等技术问题给大R造成的伤害,最后我们要想方设法给大R提供足够的小弟并提供直观的荣誉让大R带领小弟去争取,之前的文章里曾经讨论过,留住玩家最有效的,是其他玩家。这里有几个要点:1、客服对大R不能过分满足,因为人的欲望是不断膨胀的,一直满足的最终结果是无法满足。这就需要客服对大R有合理的策略和沟通方式,如果你肯在这个方面请一些心理和谈判专家多讲几堂课,然后让客服经常分享自己的心得与教训,那么你一定有超值收获。2、游戏中的“托”要有正托和反托,现在很多都是只强调反托,各种和大R比富挑衅的,却缺少和大R建立“革命友谊”的,虽然反托可以让大R充值更多,但也可能逼走大R,而正托则让大R舍不得走。“我舍不得游戏里的朋友”这句话应该是后期玩家为什么继续玩游戏的最重要理由,而大R也不例外,只不过“朋友”更多的是他的小弟。当你身上背负着更多责任和期盼的时候,如果你选择离开,会造成更多的失望和责怪(大R也不容易呀),所以防止挖墙脚的最好方式,就是给大R上很多感情保险。这需要在游戏设计上给予充分的考虑。
 
伺候得更舒服
时刻牢记大R的需求,被尊重和自我实现。游戏外的尊重就是客服团队的服务还有逢年过节的一些小礼物,中国讲究礼尚往来,这样会让大R心里觉得特别爽(君不见每次坐飞机时,那些VIP用户接到空姐送来的专属杂志时那爽歪歪的表情)。游戏内要设计足够的让普通用户认识大R,崇拜大R的渠道。比如:1、副本中给大R弄个塑像,点击可以膜拜,膜拜了给发瓶药或者加个buff,甚至可以说一句大R设定的话(通过客服在后台设置,一是可以审核,二是省掉额外的前端开发量),大R一上线看到几千号人膜拜过自己一定很爽,哪天心情好扔一万块钱开个双倍。膜拜后掉两瓶药或者buff效果时间延长一倍,大R的威名一定会被玩家互相传颂。2、给大R办个“讲座”,来参加的玩家有概率学会新技能或升级老技能。3、提供搞婚礼、生日宴、庆功宴等的场地,提供不同的规模(999999的、9999的、999的),参加的玩家有奖励,但需要门票,举办者可以发邀请函,免票。(这个场地必须是唯一性的,不要搞成副本形式可以多开的。)如果是婚礼在主城里搞个花车巡游,放个烟花。别提大R的面子有多大了。笔者不禁回忆起很久以前做《掌中三国》时,上GM号带着大礼去参加大R的游戏婚礼时那种感觉了,那哥们估计被尊重的需要得到了无比的满足。4、称号。称号有两类,一类是达到条件能得到的,全服可能同时有个几百上千个。一类是独一无二的,东邪、西毒、南帝、北丐、中神通,全服就这一个,再无其他。这比干巴巴的第一、第二、第三、第四、第五来得带感多了。况且当跨服战时,本来大R的威名其他服的没怎么听说过,但是一个36区“南帝”,立刻被尊重的感觉就完全不一样了。另外这种独一无二的称号不仅仅要求排名,还需要有一定挑战的条件,这样得到这个称号的过程就成为绝好的“自我实现”。这个过程如果引导全服的玩家参与进来,(例如:众道友齐力助十星道友XXX渡劫升仙)那真是32个赞。
 
再次小结:
在如何伺候好大R的路上,我们还有很多路要走,而这条路如何走好,无外乎摆正服务的心态、满足大R的需求、敢于创新和实践。最后祝大家的游戏大R多多,充值多多。
 
长长的各种PS
 
1、心理学上说,每个“社会群体”都会“进化”出字母缩写、暗语等独有的表达方式来区分“群体内”和“群体外”。例如90后的火星文;歌迷、球迷给偶像起的昵称(你知道小烟枪、独狼、皮波、老爹、奶茶、哥哥是谁吗?);通信行业的GSMGPRS(你知道它们的具体含义吗?)等等。游戏行业同样有着很多的暗语和缩写,免费玩家、小R、中R、大R是我们对按照付费习惯分类的用户群的缩写。
 
2、为了文章内容的严谨性,咱们扯点哲学:大学学哲学的时候,有这么两句话,一个是 “事物都在不断发展变化”,一个是“量变是质变的必要准备,质变是量变的必然结果”。扯这么大两张虎皮,就是想说:
 
1、微观上讲,玩家不是一成不变的,免费玩家并不一定永远是免费玩家。
 
2、宏观上讲,免费玩家、小R、中R、大R这些“社会角色”占游戏人口的比例和各自的心理特点是相对稳固的,需要经济、文化、科技的变革积累一定的量变才会引发质变。
 

3、每个人都是一个充满个性的独立个体,虽然社会角色可以改变人们的行为,但绝不可能控制每一个个体,因此,今天我们要讨论的心理特点,是基于统计学上的多数行为,不适于进行个体分析

转自:http://www.cocoachina.com/gamedev/designer/2014/0718/9182.html

分类: 未分类 标签:

游戏中的心理学(四):让用户掏腰包的秘密

2014年8月6日 没有评论

除了个别志向远大到超出我们理解范围的神人,大部分游戏从业者最大的希望都是自己的产品能够赚钱,那么怎样让用户更乐意掏钱呢?让我们从一个故事开始。



社会角色会影响人的行为



  1971年津巴多教授将斯坦福大学心理学系地下室改造成一个模拟监狱,并招募了一批学生志愿者,通过抽签扮演狱警和囚犯。试验造成了近乎灾难性的后果,津巴多教授的评价是:“在实验开始的时候,两组人之间没有任何区别,一个星期之后,他们之间已经变得没有共同之处了。”扮演“狱警”的学生很快变得残暴无比,他们殴打和折磨“囚犯”,让他们用手刷便池、舔鞋、头朝下倒立,而“囚犯”学生变得胆小、恐惧、顺从和自卑。实验也因为远远超出了预设的容忍范围而提前中止。



  是什么让本无差别的两组人产生了如此巨大的变化?他们从哪里学来的残暴和自卑?答案是社会角色。



  心理学对此的定义是:我们每个人都同时属于许多重叠在一起的社会群体,在每一个群体结构中我们都占有一定的位置。社会角色代表了人们对不同社会位置的行为模式的期望。



  男性、儿子、青少年、中国人、已婚者、教师、科学家、乐队指挥、消防员等等,都是社会角色。



  在斯坦福监狱试验里,当学生接受并适应了“狱警”这个社会角色后,其所对应的行为模式——对囚犯的打骂与虐待——就开始发挥作用,让他们做出了他们自己从来没有想过的恶行。而当学生接受并适应了“囚犯”这个社会角色后,恐惧、自卑就占据了大脑,他们遵从囚犯的行为模式——被狱警折磨并尽力讨好狱警以减轻折磨——忍受着从没有想过的侮辱。



两个词语对游戏用户的心理暗示作用



  让我们从令人窒息的斯坦福监狱里走出来,回过头来审视一下,网络游戏中同样存在社会角色。



  在关于社会角色的心理学定义中,特别强调了“重叠在一起的多个”。游戏用户也不例外,每个用户身上都重叠了多个社会角色。有些是现实中的社会角色,比如男性、土豪、学生,有些是在游戏中新获得的社会角色,比如帮主、小R、小白玩家。



  那么社会角色究竟对游戏用户的行为起多大的作用呢?



  首先让我们看看“女性”的作用。对于“女性”这个社会角色来说,“不喜欢重度游戏”是其行为预期之一。也许可以举出一千个理由说明女性为什么不适合重度游戏,但如果某个科学狂人完全封闭养育10000个童男童女到18岁,然后拿一款重度游戏给他们玩,我相信喜欢上这款游戏的人中男女比不会超过6:4。而不是现实社会中让人无奈的的8:2甚至9:1。这就是社会角色的力量。



  再让我们看看“三十多岁”的作用。对于“三十多岁”这个社会角色来说,“爱玩游戏”已经不是他的行为预期了。所以如果你细心的话,会发现用户年龄分布上从29岁到30岁总有一个相对较陡的下降曲线,30岁比29岁真的忙了很多吗?30岁比29岁的经济负担真的重了很多吗?其实并没有。只是因为进入三十岁以后,你有了新的社会角色,这个角色的行为预期改变了你对游戏的看法。



大R、中R、小R……与付费相关的游戏内社会角色



  如果你基本认同社会角色在游戏用户身上也发挥着巨大的作用,那么我们来讨论一下与付费相关的是哪些社会角色。



  大R、中R、小R、免费用户,这些新的社会角色随着免费+道具收费模式网游的崛起而迅速形成,并同时形成了每个角色的行为预期。



  国王、帮主、长老,这些虚拟的社会角色同样拥有对应的行为预期,只不过因为游戏之间的差异让这种预期多了不少细节上的区别。



  当玩家找到对应的社会角色后,对应的行为预期就会开始工作,除非不停的刻意提醒自己,否则让你掏腰包的就不是你的逻辑思维,而是你的社会角色了。



想要用户付费,社会角色还需“唤醒”



  是不是说我们只要做款游戏,把用户拉进来,就可以等着社会角色发挥作用然后数钱了呢?当然不是!玩家的社会角色需要在游戏过程中逐渐被“唤醒”才能发挥作用,那么首先要有不错的留存率,给“唤醒”创造足够的时间;其次你要有很好的唤醒策略来优质高效的完成“唤醒工作”;最后你要给唤醒后的用户足够的发挥空间(大R玩家1~2万就填平了所有的养成系统对游戏来说绝对是个灾难)。



  总结起来就是:留存、唤醒、发挥空间



  关于留存,本文不过多的展开讨论,只说个常理——对于“正常人”这个社会角色来说,“坚持玩一款烂游戏”肯定不会是行为预期的一部分。



  “发挥空间”是游戏策划的基本功,对于“优秀游戏策划”这个社会角色来说,“挖一个看似很浅其实很深的坑”是行为预期中的一部分。在此也不过多的展开讨论。



  那我们究竟要讨论什么呢?当然就是角色的“唤醒”。主要有以下三种方式:



1、他人在场



  心理学有个名词叫“他人在场”。例如陌生人的出现会让你停止抠鼻子这种与社会角色预期不符的行为。“他人在场”能进行角色唤醒,并且“他人在场”的数量和角色唤醒正相关。



  由于虚拟世界的特殊性,在这里还要提出一个新概念“有效在场”,只有能够观察到你特定行为的人,才算是对于这个特定行为有效在场。例如游戏主城里跑来跑去的玩家是无法让你停止扣鼻子的。但他们是你酷靓发光的装备和稀世强大宠物的“有效在场”。赢得争霸战名次的时候,观看比赛和查看排名的用户是“有效在场”。有些游戏设计排行榜参拜也是为了增加“有效在场”的人数,来唤醒大R用户的社会角色。



  这方面做得最好的,要数蓝港在线,从王者之剑那占据主城大半个屏幕的超级宠物,到神之刃的纯帅礼盒。蓝港内部有高人呀!



  关于“他人在场”有两个要点,第一是“有效在场”,如果装备升到10阶看不出任何外形变化,那么一主城的玩家就都无效了。第二是在场数量与唤醒效果的正相关,要想方设法让用户感觉有更多的“他人”能看到他的成果,说句大不敬的话,每个用户都是“人来疯”。



2、选择唤醒



  唤醒用户角色的另一个有效方法就是选择,当你来到高档KTV唱歌时,侍应生递上来的酒单上,基本都标识着至尊套餐、豪华套餐、超值套餐等等,这就是典型的选择唤醒。



  目前游戏中普遍采用的首冲大奖(还有首抽必得五星卡之类)实际上就是对小R用户的选择唤醒。让玩家看到并选择是否进行首充,能够非常有效的唤醒小R用户,因为他们善于“把钱花在性价比最高的地方。”



  但由于种种原因,对中R、大R,前期都缺乏有效的选择唤醒,游戏设计者们都遵循着循序渐进的理念,生怕过早对大R进行选择唤醒会吓跑其他用户,但我们是不是高估了大R的耐心呢?



  这方面相对来说做得比较好的是《刀塔传奇》,签到里的VIP一定等级可以领取双份,燃烧远征里边的VIP一定等级可以再玩一次都算是比较勉强的大R选择唤醒,当然如果VIP永久开启地精商人做成“地精商人即将消失,是否永久开启”的模式,效果会更为明显。



  笔者不才,在这里提一个方案以期达到抛砖引玉之效。在建立多人副本时,让玩家可以选择建立土豪房间,土豪房间在大厅里有特别炫酷的标示、完成后有更优厚的奖励。这样同时起到了“他人在场”和选择唤醒两方面的效果(当然,这样会造成玩家等待土豪房间而增加平均等待时间,并且造成玩家心目中普通房间的价值,所以说只是抛个砖)。



3、标杆唤醒



  有没有注意到,现在的游戏攻略已经分化成免费攻略、小R攻略、中R攻略、大R攻略了?这样的目的有是什么呢?



  显然一篇针对大R的攻略对小R是丝毫没有用处的,甚至直接造成用户的流失。但一篇小R攻略却能够为用户树立标杆,让用户有更清晰的目标和发展策略。



  陌生的道路、陌生的场合、新的游戏,这些都会让你感到迷茫,因为过去的经验可能帮不到你了。这个时候我们会选择跟随,这就是心理学著名的跟随效应(也叫羊群效应)。



  借用下“社会比较理论”的结论——请允许我简单介绍一下:人有进行社会比较的心理需要,而比较对象不是随机的,而是倾向于和自己相似的人比较——我们在跟随时也会选择跟随和自己相似的人。所以树立标杆时需要为不同的社会角色树立标杆,这样在选择跟随标杆时,用户就唤醒了自己的社会角色。攻略是一种有效的方式,但覆盖面比较有限,如何在游戏进程中为用户树立标杆,尤其是巧妙的区分用户并展示不同的标杆,是一个崭新而且非常有挑战性的课题,当然,如果能够很好的解决这个课题,将会得到丰厚的汇报。



  他人在场、选择、标杆。今天,你唤醒自己的土豪玩家了吗?



  PS:“他人在场”数量和角色唤醒正相关并非严格数学意义上的正相关,超出认知范围后就会失效(空旷球场上,观众席10个人还是20个人无意义,收看直播的是1亿人还是2亿人无意义。)这很好的解释了为什么在超R大R在服务器里耀武扬威的时候,小R中R甚至免费用户依然能够找到自己的乐趣。这种方式也体现出了人类心理自我保护的能力。


  关于“三十多岁”段落的分析,咱们游戏行业的人不会有太大感觉,因为“游戏从业者”这个社会角色的行为预期中有“常玩游戏”,并不会被“三十多岁”这个新社会角色影响太多。

转自:http://www.gameres.com/msg_252650.html

分类: 未分类 标签:

游戏中的心理学(三):如何突破印象关 提升初期留存

2014年8月6日 没有评论

关于如何提升游戏的早期留存,可谓是众说纷纭。在众多理论和观点中,史玉柱的”过三关”理论很好的总结了提升早期留存的关键点和策略,今天笔者试着从心理学的角度对史老师三关理论中的印象关做一个比较详细的分析。史老师的印象关强调的是画面的精美、操控的舒适、音效的质量,其中,尤以画面精美为最。史老师的这个观点凝结了他的人生智慧,也暗合心理学的要义,可谓是一语道破天机。



  首先我们从“游戏初期画面为什么特别重要”这个方面来分析。在游戏初期,玩家刚刚下载了你的游戏,对游戏处于完全不了解的阶段。在这个阶段,心理学有一个命题“我们需要多长时间认知新物?”一位心理学家讲了一个故事:



  我们远古的祖先在东非大草原上结队前行,突然,一种新的动物向它们跑了过来,看体型和动作有点像剑齿虎,但没有那夸张的牙齿,这时远古人类中的一部分靠潜意识迅速做出结论——“它会吃我”,然后撒腿就跑,而另外一部分则在观察和思考,试图了解更多——很不幸,思考者们成了这只老虎的猎物。



  在人类几百万年的进化之路上,这样的故事不断发生,直到对新事物认知缓慢的那部分原始人类基因被无情的彻底淘汰。



  这个残酷的故事解答了“我们需要多长时间认知新事物”的问题——答案是令人吃惊的短短数秒钟! 这个神奇的过程是怎样进行的呢?首先我们的大脑里有一个庞大的分类库(其中一部分来自于基因遗传、另外大部分来自后天习得。)然后我们会对遇到的新事物进行归纳,归纳到近似的分类库中并将该库的共性赋予新事物。



  现在我们回到游戏上来,玩家看到了你的游戏(新事物),会如何进行归纳呢?因为电子游戏都是后天习得,玩家玩过的游戏种类和玩游戏的时间、经历五花八门各不相同,所以没有比较统一的游戏分类库。但有一个基础的维度一定是每个人都有的,那就是好游戏和烂游戏。



  在数秒钟内认知一个游戏的好与烂,靠什么?只能靠画面!



  刚刚我们讲到过,对新事物的归纳是基于什么标准呢?相似度!那么对画面精美的游戏进行归纳时,它和好游戏库相似度高还是烂游戏库相似度高呢?答案显而易见。于是,玩家把画面精美的游戏归到好游戏库中,并把好游戏库的共性——好玩,值得玩下去——赋予了它。



  这种通过相似归纳来进行快速认知的方法在心理学上称为“刻板印象”(Stereotypes),有这个理论撑腰,你是不是可以放心大胆的把最好的美术力量投入到游戏初期甚至下载之前的宣传和Logo上了?不要怕,绝对值得!



  这时有细心的朋友可能要问了:“照这个理论除了前几秒玩家看到的画面以外,是不是后边随便画成什么样都无所谓了呢?”别急,如果仅仅靠“刻板印象”的话,人类是无法最终成为地球主人的!在建立了“刻板印象”之后,潜意识会对这一印象进行修正,不然就真的是“人生若只如初见”了。



  此时应该会有鸡蛋扔上来——坑爹呀,后边还会修正,那后边还不是一样得好好画?什么工作量都没有省呀!



  别急,还没说完呢。



  上篇文章我们提到过玩家认定一件事情后,会注意相符合的结果,忽略相违背的结果(上篇文章提到过玩家参与性提高后会注意自己选对的结果而忽略自己选错的结果),这在心理学上称为“确认偏向”(confirmation bias)。当“刻板印象”把游戏归纳为一个好游戏后,在继续玩的过程中,潜意识会进行校验和修正,但这个修正受到“确认偏向”的强力影响。比如有很棒的音效,潜意识会觉得“你看,这是好游戏吧,这动作的音效多棒呀!”有个怪物移动动作不太协调,潜意识却忽略或淡化了,“这个怪物走路有点怪,不过who
care!” “确认偏向”的威力超乎想象,我们生活中常说的 “第一印象”如何重要,其原因就是因为“确认偏向”。




  现在让我们回到几百万年前的东非大草原上,我们可怜的远古祖先在危机四伏的环境中艰难的繁衍生息,他们时刻都在面对着各种未知的危险和各种新鲜事物。这也注定了他们没有太多时间和精力对已经认知的事物进行长期的修正工作,所以在不太长的一段时间后,对该事物的认知就基本定型了,之后如果不出现重大变化就不会再进行修正(什么叫重大呢?比如:长着长脖子的动物突然开始吃肉了!以后见了它要跑。)。



  总结起来最符合心理学的设计理念应该是:完美的开始+优秀的前15分钟+及格的后期(美术、UI、音效、操控等)。完美的开始用于通过“刻板印象”将游戏分到好游戏库,优秀的前15分钟在“确认偏向”的帮助下完成对游戏认知修正,及格的后期保证不出现重大的变化重新激活认知过程。这种理念主导下,你的工作量大约是“全完美”(完美开始+完美15分钟+完美后期)的20%,但却能达到“全完美”实际效果的80%甚至更多!这种设计理念是中小型开发团队毫无争议的首选。



  接下来我们从另一个角度分析一下,为什么游戏中后期画面不那么重要?这里我们要引用的是著名的“变化盲视”(Change Blindness)理论。“变化盲视”是指当你关注某一事物时,视野内其他事物的变化很难引起你的注意。该现象相关的实验有很多,并且很多拍成了视频,有兴趣的朋友可以百度一下。(PS:变化盲视也是魔术师最爱使用的招数。)



  我们以ARPG为例,玩游戏一段时间后,玩家在战斗过程中的关注点已经转移到如何躲避怪物攻击、如何走位引怪等方面,当你和一群怪物艰苦缠斗的时候,你关注的是怪物的移动和攻击,这个时候你根本不会关注游戏场景的变化,甚至怪物服饰的变化也会被完全忽略。



  你可以回忆一下你最近在玩的游戏第六章和第七章的战斗场景是否一样?我猜你多半需要打开游戏去专门看一下才知道答案。



  对于开发者来说,在这个阶段去精心绘制场景里的一根图腾或者为怪物设计一个漂亮的帽子,是不是有点好钢没用在刀刃上的感觉?



  “刻板印象”+“确认偏向”+“变化盲视”,这些理论很好的解释了史老师集中力量狠抓游戏开始阶段的原因。我们不得不佩服史老师深邃的人生智慧和独到的眼光,在此向史老师表示最诚挚的敬意,您真的太牛了!



  PS:额外插一句,在开始的15分钟里,如果有些并非必要的东西你没能力做好,那就宁可不要做。玩家的潜意识是发现不了你缺东西的,但你展示给玩家的东西就会被用来做认知修正!做的不好可是要减分的哦!


  但是,也不要滥用这个办法,玩家毕竟不是傻子,缺的东西太多,他肯定会察觉到,到时候造成严重流失可就谁都帮不了你!

转自:http://www.gameres.com/msg_250629.html

分类: 未分类 标签: