一款RPG游戏中的剧情脚本应该怎么如何自己实现一款ide

613被浏览38740分享邀请回答8619 条评论分享收藏感谢收起想成为游戏脚本作者,你需要先意识到游戏的与众不同 | 天了噜!小组 | 果壳网 科技有意思
33204人加入此小组
罗伯特·麦基在《故事》里告诉所有那些想知道好莱坞编剧要诀的人:“最优秀的作品不单揭示人物真相,而且在讲述过程中表现人物本性的发展轨迹或者变化,无论它们是好是坏。这种人物的变化就是人物弧光。 弧光(arc light),一种持续的耀眼亮光,有时具有辉光弧线的外貌,在电路断开时形成。即电弧发出的光,光度很强,带蓝色。——百度百科(喂)事实上,不单是好莱坞的编剧,任何一个想要写出好故事来的人恐怕都有意无意地参照了这条标准,游戏脚本作者们当然也不例外。但与传统的电影、小说、漫画这样的媒介不同,即便概念相通,游戏也自有它的一套准则。亚历山大·M·自由人(Alexander M. Freed),曾经的Boiware奥斯丁工作室首席作家,在发现有些准则似乎并不是那么清晰的时候,决定写一篇文章探讨一下其中的一个主题:如何利用分支叙述来塑造有意义的玩家角色弧光。 《质量效应:仙女座》即出自其手大部分的电子游戏(特别是像质量效应、杀出重围:人类革命这样基于选择的RPG游戏)就结构而言都采用了非常传统的叙事。一款RPG游戏的中心通常都是一个主角在让人难以忍受的环境下做出各种艰难的选择。然而许多的RPG其实并没有成功地塑造一个吸引人的人物弧光,理由也很简单:当玩家能够控制主角和他/她所做的选择时,他/她可不会考虑怎么去塑造一个吸引人的人物弧光,玩家会沉浸在角色中,满足那个当初驱使自己买下这款游戏的欲望(“我想跟邦德一样酷炫。”啦或者“我想做个最可怕的罪犯。”这种)。所以显然,为了满足自己这个人设,玩家很可能会把同一类决定贯穿始终,英雄就一路做个英雄,恶棍则一路做个恶棍——除非,有什么理由让他们改变。亚历山大指出,有太过热心的设计者因为执着于创造富有吸引力的人物弧光而无视了玩家的选择将某一系列的“改变”强加到玩家身上。角色并非在玩家操控下做出了某种反应,而玩家的感受则是“我的角色绝对不可能说这种话!”。于是玩家和角色之间的共情被破坏了。为了避免这样的错误,同时又能够通过角色弧光来讲述动人的故事,他认为重点在于要为玩家创造“困境”。这种困境会和那些玩家已经很能够接受了的价值观产生冲突,让他们觉得不适。它们能够测试玩家,逼迫他们进行改变,或者积极地抗拒改变。比如玩家是一个准军事化组织的一员,本身十分正直,对组织忠诚,与同僚关系良好,但是当这个组织做的事开始变得善恶难分的时候,“做正确的事”和“支持同伴”逐渐发生了冲突,而玩家也必须要重新审视自己的决策过程。《杀出重围:人类革命》显然是在不断用机械植入的伦理问题来困扰玩家但是这样的困境要让玩家觉得有多“不适”也同样是一个问题。如果玩家只是想坐在电视或者电脑前头,开开枪杀杀人,感受一下肾上腺素的刺激什么的,游戏为什么要设计成为难他们的样子?这样的卖点何在?其实,即便是很多娱乐向的游戏也有许多对主角而言非常不友好的时刻(比如天行者失去了他的家人之类),而正是这些时刻改变并定义了这个角色。如果能够善加利用,这些时刻对直接与故事互动的玩家所造成的影响将远远超过对一个被动的读者或者观看者的影响。 黑曜石的暴君同样出自其手,我想玩过的人对其中形形色色让人难受的决策记忆犹新那么这个“不适”的度在哪里?亚历山大也不能下定论。唯一肯定的是,不要让玩家的不适超过必需的限度。然后他还谈到了如何通过旁白来塑造人物。像《史丹利的寓言》那样直接搞个旁白给人一点点描述玩家举动的作品是特例,大部分游戏的设计者必须要找到别的方式来指出玩家的决定如何影响了故事,否则,这个决定就该被视为不存在。同样,当玩家的角色产生了某种内心挣扎,它需要被反复且清晰地提及,不然它就会被玩家所遗忘。 亚历山大从一个设计者和剧本作者的角度讨论了游戏要怎么来讲述一个好故事,用现实中的栗子和自己的经验表达了游戏和电影、小说这些传统媒介的不同。全文专业性较强,比较枯燥,其实对大部分玩家来说也不是很实用。但我觉得这对关注中国游戏行业的人而言是有启示的。以非常有代表性和文化特色的“三把剑”(其实三把剑是哪三把是有争议的,但是重点不是这个,我就为了方便借用一下概念)来说,仙剑、轩辕剑和古剑系列,其本质是用游戏的方式给玩家讲述了一个故事。如果说前代几作的成功在于它们所讲述的故事深刻且感人,那么近年来这几作的弱点恐怕也正在于此。玩家的选择——如果有的话——其实并不重要,最多决定故事的结局,而不会影响游戏的走向,更不会改变角色的设定。这并非不好,毕竟不是所有的游戏都需要强调自由度,三剑也有其固定的受众。但我前几日在玩《古剑2》的时候(对,之前steam大折扣忍不住加一了),虽然觉得它终于改变了的战斗系统即使粗糙也值得展上,不过面对几乎就是动画片段的剧情段落时也确实因为这种被束缚感而产生了厌烦的心理,而作为仙剑迷的粉丝加成其实也早在仙五和古剑1发售的时候消耗殆尽了。近期登上Steam且价格良心的古剑系列,虽然因为bug和成就弹窗问题遭到了吐槽,但二代也达到了大部好评。人说穷则变,变则通,如三剑这样的传统仙侠游戏恐怕还没到穷尽的时候,但它坚持讲好一个故事的传统某种程度上也限制了它们本身的发展。与电影观众或者小说读者相比,游戏玩家的深入程度是最高的,它赋予了玩家操作和选择的权利,其实也应该剥夺作者同样比例的权利。近几年来的互动电影向游戏,如《暴雨》和《直到黎明》同样强调剧情,但是比起平铺直叙,这些游戏迫使玩家进行积极的探索和选择,以此来“揭开”故事的全貌。它们把讲完这个故事的责任推到了玩家的头上,以此来加强玩家的体验。而三剑系列则受困于自己为自己设下的界限,自己一力承担起了这个责任。是好是坏,我不敢说。但中国游戏可以走更广的路,这是肯定的。毕竟当年的我们可不仅有仙剑轩辕剑,也曾经诞生过如《金庸群侠传》这样高自由度的作品(这种时候要吹一波头很铁怎么都不肯打折的河洛,他们的《侠客风云传》和《前传》虽然评价也没有高到飞起,甚至《前传》因为bug太多而被打了很多差评,但无论是当初《群侠传》的开拓还是如今《风云传》的破局,他们的尝试还是值得尊重的)。就是不肯打折的侠客风云传,可以说是《金庸群侠传》的精神续作在版权意识觉醒的现在,作为一个普通玩家,希望能看到更多眼光长远的国产游戏,也希望下一次我们为此掏钱的时候,并不是因为要支持国产,而单纯是因为“这是一款好游戏,我想玩。” Ref:图片来源:网络
+ 加入我的果篮
只能说,不是一类玩家,不玩一样游戏毕竟GAL一样有人玩得很开心(不过其实能不能算“玩”我也是抱疑问的)国产RPG三剑本质上就是日式RPG的路子,大致固定的剧情走向,早已刻画好的人物形象。游戏重点就是用一个还算不错的游戏系统以及画面来呈现一个还算可以的故事,从而摆脱单纯翻书看插图按回车空格的无聊感(但其实对有爱的人来说,翻书看插图并不无聊)只不过,日系RPG的确太消磨耐心了,尤其是对一些本身工作节奏就紧张的人来说,面对一个几乎没有巨大分支的剧情,能看动画为什么还要去打那反反复复的战斗流程?所以视频通关、快节奏的PFS以及西方风格RPG日渐流行也是意料之中
(C)2017果壳网&&&&京ICP证100430号&&&&京网文[-239号&&&&新出发京零字东150005号&&&&
违法和不良信息举报邮箱:&&&&举报电话:匿名用户不能发表回复!|
每天回帖即可获得10分可用分!小技巧:
你还可以输入10000个字符
(Ctrl+Enter)
请遵守CSDN,不得违反国家法律法规。
转载文章请注明出自“CSDN(www.csdn.net)”。如是商业用途请联系原作者。注意:本系列教程为长篇连载无底洞。半路杀进来的朋友,假设看不懂的话。请从第一章開始看起。文章文件夹请点击以下链接。一,内容预览算起来,游戏脚本系列文章已经非常久没更新了,尽管该系列文章更新缓慢,可是确实还是能够帮到一些朋友。前段时间。仅仅由于做毕业设计通过邮件联系我的就有4位学生。有鉴于此。我还是挤点儿时间来继续慢慢更新一下了。另外,我想再声明一下,眼下该脚本引擎还处在移植开发阶段,所以功能优先,当中有大量须要优化的地方还没有处理,所以在手机上測试的时候,在一些性能比較差的手机上。假设效果不太好的话,不要奇怪。本节来介绍一下怎样通过脚本来控制正在执行的游戏。一个RPG游戏中会触发各种各样的剧情,让某个人物的动作改变,或者让某个人移动到还有一处,或者让某个人物从战场上消失,或者会播放一段动画,或者会切换游戏场景,等等吧,这些都要通过脚本来动态的控制。我在序章中就已经说了,一个优秀的游戏脚本,通常能控制游戏中的一切。本次先来实现一下几个游戏指令,以后会再进行扩展。?人物角色依照绝对坐标进行移动?人物角色以自己为參照物,依照相对坐标进行移动?人物角色以指定人物为參照物,依照相对坐标进行移动?人物方向和动作的改变(等待动作结束)?人物方向和动作的改变(不等待动作结束)?人物角色进入场景?人物角色离开场景当然,还有最主要的对话游戏指令,这个已经在之前实现过了,所以能够直接使用。还有我上面提到的或者没有提到的其它指令。以后可能会继续进行扩展。可是RPG的游戏指令是列举不完的,大家须要做的是掌握方法。然后依据自己的实际需求来进行扩展。二。功能屏蔽正在执行的游戏中。假设没有触发特定的剧情,玩家们使用鼠标或者键盘来进行游戏。可是一旦触发了剧情。在剧情结束之前,就须要游戏依照脚本指令来进行,这时候可能须要屏蔽原来的鼠标或者键盘功能,比方点击地图,主角会发生移动,等等,或者控制菜单有时候也会禁用,当然。这个看自己的须要了。总之,须要这么一条脚本指令来屏蔽一些功能,然后让游戏依照制作者期望的方向进行发展。这个功能实现起来并不难,仅仅须要设定一个变量,游戏中的须要屏蔽的一些功能。依据这个变量的值来决定是否有效,然后通过脚本来改变这个变量的值来实现功能屏蔽。先来规定以下的脚本RPGRunMode.set(1);
RPGRunMode.set(0);參数为1的时候表示功能屏蔽,參数为0的时候表示解除功能屏蔽。首先,在LRPGObject.js中建一个变量LRPGObject.runMode =默认是false,表示剧情模式没有开启,也就是不须要屏蔽功能,反之。则表示开启了剧情模式。也就须要屏蔽功能了。而脚本解析部分也能够非常轻松的实现。/*
* LRPGRunMode.js
LRPGRunMode = function(){};
LRPGRunMode.analysis=function(value){
var start = value.indexOf("(");
var end = value.indexOf(")");
var params = value.substring(start+1,end).split(",");
switch(value.substr(0,start)){
case "RPGRunMode.set":
LRPGObject.runMode = (parseInt(params[0]) == 1);
LGlobal.script.analysis();
LGlobal.script.analysis();
};然后。在须要屏蔽的功能的代码中,增加以下一句话就能够实现屏蔽了。if(LRPGObject.runMode)三,人物角色移动指令控制人物角色移动的时候,大致能够分为以下三种情况。?人物角色依照绝对坐标进行移动?人物角色以自己为參照物,依照相对坐标进行移动?人物角色以指定人物为參照物。依照相对坐标进行移动以下一个个来看1,首先看一下[人物角色依照绝对坐标进行移动],就是向绝对坐标的移动,规定脚本例如以下。//角色ID,坐标x,坐标y。是否等待
RPGCharacter.moveTo(2,60,13,1);先不说这个脚本怎样进行解析。首先,先在MapController中增加一个控制角色的行走的函数,然后在解析脚本的时候仅仅须要调用这个函数即可了。控制人物行走的代码,在实现控制主角行走的时候已经有了,如今仅仅须要稍加调整,把主角换成指定的角色(角色ID)就能够了,改动后的部分代码例如以下。MapController.prototype.characterMoveTo = function(chara,cx,cy,callback){
var self =
chara = self.getCharacter(chara);
if(!chara)
if(chara.hasEventListener(Character.MOVE_COMPLETE)){
chara.removeEventListener(Character.MOVE_COMPLETE);
var coordinate = chara.getTo();
var fx = coordinate[0] , fy = coordinate[1];
var returnList = self.query.queryPath(new LPoint(fx,fy),new LPoint(cx,cy));
if(returnList.length & 0){
chara.setRoad(returnList);
if(callback){
chara.addEventListener(Character.MOVE_COMPLETE,callback);
MapController.prototype.getCharacter = function(value){
var self =
if(LString.isInt(value)){
var childList = self.view.charaLayer.childList,
for(var i=0,l=childList.i&l;i++){
child = childList[i];
if(value != child.index)
}else if(typeof value == "object"){
};有了这个函数。解析前面的脚本就简单多了。看以下的解析代码。LRPGCharacter.moveTo = function (value,start,end){
var params = value.substring(start+1,end).split(","),
if(params.length == 3){
wait = (parseInt(params.pop()) == 1);
//params:index,x,y
LRPGObject.RPGMap.characterMoveTo.call(LRPGObject.RPGMap,params[0],parseInt(params[1]),parseInt(params[2]),LRPGCharacter.getMoveCallback(wait));
LRPGCharacter.getMoveCallback = function (wait){
var lineValue, callback = LGlobal.script.analysis.bind(LGlobal.script);
if(!wait && LGlobal.script.lineList.length & 0){
lineValue = LMath.trim(LGlobal.script.lineList[0]);
if(lineValue.indexOf("RPGCharacter.move") == 0){
callback =
LGlobal.script.analysis();
};上面的LRPGObject.RPGMap.characterMoveTo.call就是调用前面已经准备好的角色移动的函数了。须要说明的时候。里面还出现了一个新的函数getMoveCallback。这是由于。有时候。人物角色不是一个一个发生移动的,可能须要同一时候让多个人物角色发生移动,这种话,一行移动脚本解析完之后。假设该行脚本设定为不须要等待,则须要检查一下下一行的脚本是不是也是人物移动,假设是的话,就马上解析下一行脚本,如此循环。假设脚本指令发生了改变,或是设置了须要等待。就将最后一个移动指令的回调函数设定为LGlobal.script.analysis。这样最后一个人物移动完之后,就会自己主动解析下一行的脚本了。效果例如以下。2,接着看[人物角色以自己为參照物,依照相对坐标进行移动],规定脚本例如以下。//角色ID,相对坐标x,相对坐标y
RPGCharacter.move(2,-4,0);相同,先在MapController中增加对应的函数。由于已经有了前面的characterMoveTo函数,所以仅仅须要计算好对应的绝对坐标,然后调用characterMoveTo函数即可,代码例如以下。MapController.prototype.characterMove = function(chara,cx,cy,callback){
var self =
chara = self.getCharacter(chara);
self.characterMoveToCharacter(chara,chara,cx,cy,callback);
};接下来是脚本解析。LRPGCharacter.move = function (value,start,end){
var params = value.substring(start+1,end).split(","),
if(params.length == 3){
wait = (parseInt(params.pop()) == 1);
//params:index,x,y
LRPGObject.RPGMap.characterMove.call(LRPGObject.RPGMap,params[0],parseInt(params[1]),parseInt(params[2]),LRPGCharacter.getMoveCallback(wait));
};效果例如以下。3。再看[人物角色以指定人物为參照物,依照相对坐标进行移动]。规定脚本例如以下。//移动角色ID,參照角色ID,相对坐标x。相对坐标y
RPGCharacter.moveToCharacter(2,1,0,4);相同,先在MapController中增加对应的函数MapController.prototype.characterMoveToCharacter = function(chara,toChara,cx,cy,callback){
var self =
chara = self.getCharacter(chara);
toChara = self.getCharacter(toChara);
var coordinate = toChara.getTo();
self.characterMoveTo(chara,coordinate[0] + cx,coordinate[1] + cy,callback);
};脚本解析。LRPGCharacter.moveToCharacter = function (value,start,end){
var params = value.substring(start+1,end).split(","),
if(params.length == 4){
wait = (parseInt(params.pop()) == 1);
//params:index,index2,x,y
LRPGObject.RPGMap.characterMoveToCharacter.call(LRPGObject.RPGMap,params[0],parseInt(params[1]),parseInt(params[2]),parseInt(params[3]),LRPGCharacter.getMoveCallback(wait));
};效果例如以下。4。多个人物同一时候移动的时候,脚本例如以下。RPGCharacter.move(1,4,0,0);
RPGCharacter.move(2,4,0,0);效果如图。四,人物角色方向和动作改变指令通常动作发生改变的时候,有以下两种情况的话,应该就够用了。?人物方向和动作的改变(等待动作结束)?人物方向和动作的改变(不等待动作结束)规定脚本例如以下。//參数:角色ID,动作,方向(或者角色ID),动作是否循环。是否等待
RPGCharacter.changeAction(2,move,up,0,1);
RPGCharacter.changeAction(2,move,left,1);
RPGCharacter.changeAction(1,stand,2,1);当第三个參数指定为数值型的时候,表示转向该指定角色。而最后一个參数必须是当动作不循环的时候才有效,否则默认不等待。先在MapController中提供动作方向改变的函数。MapController.prototype.setActionDirection = function(chara,action,direction,loop,callback){
var self =
chara = self.getCharacter(chara);
if(LString.isInt(direction)){
var toChara = self.getCharacter(direction);
var coordinate = chara.getTo();
var coordinateTo = toChara.getTo();
var angle = Math.atan2(coordinateTo[1] - coordinate[1],coordinateTo[0] - coordinate[0])*180/Math.PI + 180;
if(angle &= 22.5 || angle &= 337.5){
direction = CharacterDirection.LEFT;
}else if(angle & 22.5 && angle &= 67.5){
direction = CharacterDirection.LEFT_UP;
}else if(angle & 67.5 && angle &= 112.5){
direction = CharacterDirection.UP;
}else if(angle & 112.5 && angle &= 157.5){
direction = CharacterDirection.RIGHT_UP;
}else if(angle & 157.5 && angle &= 202.5){
direction = CharacterDirection.RIGHT;
}else if(angle & 202.5 && angle &= 247.5){
direction = CharacterDirection.RIGHT_DOWN;
}else if(angle & 247.5 && angle &= 292.5){
direction = CharacterDirection.DOWN;
direction = CharacterDirection.LEFT_DOWN;
chara.setActionDirection(action,direction);
if(callback){
callback();
var fun = function(){
chara.actionObject.anime.stop();
chara.PLETE,fun);
callback();
chara.PLETE,fun);
};上面的代码并不难理解。须要解释一下的是当传入的方向是角色ID的时候,须要通过当前人物角色和指定人物角色的位置。来推断人物详细应该转向哪个方向,上面的代码是通过计算两个人物之间的向量夹角。与八个方向的角度进行比較,接近哪个方向就设定为对应的方向。接着,是脚本解析的代码。LRPGCharacter.changeAction = function (value,start,end){
var params = value.substring(start+1,end).split(","),
//params:index,action,direction,loop,wait
params[3] = (parseInt(params[3]) == 1);
if(params.length == 4){
wait = (parseInt(params.pop()) == 1);
params.push(LRPGCharacter.getActionCallback(wait));
LRPGObject.RPGMap.setActionDirection.apply(LRPGObject.RPGMap,params);
LRPGCharacter.getActionCallback = function (wait){
var lineValue, callback = LGlobal.script.analysis.bind(LGlobal.script);
if(!wait && LGlobal.script.lineList.length & 0){
lineValue = LMath.trim(LGlobal.script.lineList[0]);
if(lineValue.indexOf("RPGCharacter.changeAction") == 0){
var start = lineValue.indexOf("(");
var end = lineValue.indexOf(")");
var params = lineValue.substring(start+1,end).split(",");
if(parseInt(params[3]) == 0)
callback =
LGlobal.script.analysis();
};上面的getActionCallback函数是为了实现同一时候处理多个人物的动作或方向,当指定动作不等待的时候,会紧接着解析下一行脚本。效果如图。同一时候控制多个角色的动作的时候,脚本例如以下。RPGCharacter.changeAction(1,stand,2,1,0);
RPGCharacter.changeAction(2,stand,1,1,0);效果例如以下。五,最后人物角色进入和离开场景的指令首先规定脚本例如以下。//人物角色离开场景
//參数:角色ID
RPGCharacter.remove(2);
//人物角色进入场景
//參数:角色ID。方向,动作,坐标x。坐标y,是否可控
RPGCharacter.add(2,stand,down,52,13,false);对应的MapController中处理例如以下。代码非常easy就不多解释了。MapController.prototype.addCharacter=function(index,action,direction,x,y,ishero,callback){
var self =
self.view.addCharaLayer(index,action,direction,x,y,ishero);
if(typeof callback == "function")callback();
MapController.prototype.removeCharacter=function(index,callback){
var self =
self.view.removeCharaLayer(index);
if(typeof callback == "function")callback();
};在MapView中,增加角色是原来就有的,以下仅仅看一下移除角色部分。代码例如以下。MapView.prototype.removeCharaLayer=function(index){
var self =
var childList = self.charaLayer.childList,
for(var i=0,l=childList.i&l;i++){
child = childList[i];
if(index == child.index){
self.charaLayer.removeChildAt(i);
};这部分的脚本的解析部分尤其简单,加上本节其它代码,完整的脚本解析能够直接打开以下的连接看一下。人物角色离开场景效果人物角色进入场景效果六。小结还是我刚開始说的那样,RPG的脚本指令是列举不完的,关键是了解本节所介绍的方法,然后就能够自己依据须要进行扩展了。本节所实现的功能,能够打开以下连接,点击关羽右边的那个NPC来进行測试。最后,给出本次的代码下载:预告:RPG部分下一节可能会讲一下变量的应用,简介一下游戏的读档和存档的原理。《游戏脚本的设计与开发》系列文章文件夹本章就说到这里,欢迎继续关注我的博客转载请注明:
阅读(...) 评论()613被浏览38740分享邀请回答my.oschina.net/u/186074/blog/528266当然上面描述了这些,只解决了一个底层问题,即如何组织一个剧情故事;而上层问题是,如何设定在合适的条件下,触发某个剧情,设计思路如下,首先设定某个剧情,在满足哪些条件的情况下触发;例如:满足和Npc1对话过,和Npc2对话过,要和Npc3对话,背包里面有物品item1;而剧情触发经常是在处理要和Npc3对话时,对条件进行检测;因此需要在系统中存系统的条件变量,例如主线任务进行到某一步的条件变量;当和某个NPC对话时,触发剧情检测,若条件变量满足,则触发剧情。因此需要设计:主动触发剧情检测的观察点,系统的条件变量,以及 满足哪些条件触发某个剧情相关配置。举几个检测点的例子:1:在和某NPC对话时检测剧情条件是否成立;2:在进入场景的时候检测剧情条件是否成立;3:在进入某个区域时检测剧情条件是否成立;316 条评论分享收藏感谢收起9314 条评论分享收藏感谢收起查看更多回答}

我要回帖

更多关于 剧情脚本 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信