+ -
当前位置:首页 → 问答吧 → [原创]首发一个简单的网页游戏框架,类似VDG的类型

[原创]首发一个简单的网页游戏框架,类似VDG的类型

时间:2010-05-22

来源:互联网

几个星期前花了大概2~3天把游戏框架写完了, 剩下的就是素材的事了, 本来是想做个类似VDG的效果的(Vitual Date Girl)
发觉其实只要有很好的素材和故事及线索等等, 就可以做一个不错的侦探型的游戏.
也可以做导向型的控制, 当然我目前的版本主要是基于图片的更新显示以及相关的对话或选择信息

大概说明下, 主要分3块:
一个HTML文件, 主显示游戏或你用来做其他导向型应用的显示页面
一个XML文件, 用来存放该游戏所需要的统计属性变量, 流程控制, 场景信息, 图片, 对话, 选择, 用户交互控制等等
脚本部分: 一个CSS控制样式, 一个JS用来对XML里的各种信息和控制做相应的显示动作

如果要用现有的框架做你自己想要的效果或游戏, 只要更改XML文件即可, 我稍微说明下XML,
ROOT 标签下 分2块:
CONF: 游戏所需要的基本变量设置, 所有变量这设置, JS会自动初始化, 可以更改或添加VAR标签, 但是这部分要用E文(目前只开发了个VARIABLES, 也可以开发其他的, 但是JS里要修改下调整相应的操作.
DATA: 存放场景, ID标签是各个场景的ID, 不能重复, DIALOG是显示在最上面的对话或标题, IMAGE 就是图片, VALUE的格式符合IMG SRC的格式就可以了, HOTSPOT 定义图片热点作为用户互动, CHOICE标签作为最下面的选择操作. PREREQUISITE标签是该场景要显示的先决条件,不满足的话, 就去不了该场景.

关于属性部分:
ACTION: 主要格式 动作.变量名字1,变量名字2...,变量名字N[.数值], 某些时候最后的 .数值可以不写, 动作在JS里可以自己定义或增加.
TARGET: 就是点了后, 显示哪个场景
VALUE 或 TIP 主要就是显示在页面上的字

注意如果没有 HOTSPOT, 或 CHOICE的话 父级标签需要保留.

另外: CSS可能需要修改, 貌似FF, IE里图片大小位置可能会有问题.

在线样板:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title></title> <script type="text/javascript"> var gameVariables; var xmlSceneID; var gameScene=new Object(); gameScene.previous=new Object(); gameScene.current=new Object(); gameScene.previous["name"]=null; gameScene.previous["index"]=null; gameScene.current["name"]=null; gameScene.current["index"]=null; var preChkFlag=new Object(); preChkFlag["name"]=new Object(); preChkFlag["index"]=new Object(); function buildGameContent(sTarget, sAction){ var idx, sTrgt; var idxTarget; idxTarget=xmlSceneID[sTarget]; idx=idxTarget; if (sAction != "" && sAction != null && sAction !="null"){ updateUserAction(sAction); } idx=preChkGameScene(sTarget, idxTarget); buildHTMLContent(idx); if (gameScene.current["index"]!=idx){ gameScene.previous["name"]=gameScene.current["name"]; gameScene.previous["index"]=gameScene.current["index"]; gameScene.current["name"]=xmlSceneID[idx]; gameScene.current["index"]=idx; } } function buildHTMLContent(idx){ document.getElementById("divDialog").innerHTML=xmlNodes[idx].getElementsByTagName("dialog")[0].childNodes[0].nodeValue; document.getElementById("divMain").innerHTML=dispImage(xmlNodes[idx].getElementsByTagName('image')[0].getElementsByTagName('name')[0].getAttribute("value"), xmlNodes[idx].getElementsByTagName("hotspot")); document.getElementById("divChoice").innerHTML=dispChoice(xmlNodes[idx].getElementsByTagName("choice")); } function dispImage(imgURL, obj){ var strMap=""; var strImg="<table style='border:none'><tr><td style='border:none;text-align:center;'><img id='showImg' src='"+imgURL+"' border='0' usemap='#imgMap' /></td></tr></table>"; if (obj.length > 0) { strMap="<map name='imgMap'>"; for (var i=0;i<obj.length;i++) { if (chkImgHotspot(obj[i])){ var mapShape=obj[i].getAttribute("shape"); var mapCoords=obj[i].getAttribute("value"); var mapTip=obj[i].getAttribute("tip"); var mapAction=obj[i].getAttribute("action"); var mapTarget=obj[i].getAttribute("target"); mapAction=(mapAction==null || mapAction=="")?"null":mapAction; strMap+="<area shape='"+mapShape+"' "; strMap+="coords='"+mapCoords+"' "; strMap+="title='"+mapTip+"' "; strMap+="href='#' onclick='buildGameContent(\""+mapTarget+"\", \""+mapAction+"\");' />"; } } strMap+="</map>"; } return strImg+strMap; } function chkImgHotspot(obj){ switch(obj.getAttribute("shape")){ case "rect": case "circ": case "poly": case "default": return true; break; default: return false; } } function dispChoice(obj){ if (obj.length == 0){ return ""; } var strTbl="<table style='margin-top: 10px; width: 96%;'><tr>"; var strCell=""; for (var i=0;i<obj.length;i++){ var attType=obj[i].getAttribute("type"); var attValue=obj[i].getAttribute("value"); var attAction=obj[i].getAttribute("action"); var attTarget=obj[i].getAttribute("target"); attAction=(attAction==null || attAction=="")?"null":attAction; if (attType=="btn"){ strCell="<td><button onclick='buildGameContent(\""+attTarget+"\", \""+attAction+"\");' style='padding: 4px 16px; color: red; font-weight: bold;'>"; strCell+=attValue+"</button></td>"; break; } strCell+="<td class='curPointer' onclick='buildGameContent(\""+attTarget+"\", \""+attAction+"\");' >"; strCell+=attValue+"</td>"; } strTbl+=strCell+"</tr></table>"; return strTbl; } function getXMLElements(xDoc, xPath){ if (window.ActiveXObject){ return xDoc.selectNodes(xPath); } else if (document.implementation && document.implementation.createDocument){ var resNodes=[]; var xmlEvaluator=new XPathEvaluator(); var xmlNSResolver=xmlEvaluator.createNSResolver(xDoc.ownerDocument==null?xDoc.documentElement:xDoc.ownerDocument.documentElement); var xmlIterator=xDoc.evaluate(xPath, xDoc, xmlNSResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); var resNode; while (resNode=xmlIterator.iterateNext()){ resNodes.push(resNode); } return resNodes; } else { return null; } } function mapSceneID(xNodes){ var sScene; var objSceneMap=new Object(); for (var i=0;i<xNodes.length;i++){ sScene=xNodes[i].getElementsByTagName("id")[0].getAttribute("value"); objSceneMap[sScene]=i; objSceneMap[i]=sScene; preChkFlag["name"][sScene]=true; preChkFlag["index"][i]=true; } return objSceneMap; } function preChkGameScene(sTarget,idxTarget){ if (preChkFlag["index"][idxTarget]){ var preReqName, preReqType, preReqValue1, preReqValue2, varGameValue, tmpIndex; var preReqList=xmlNodes[idxTarget].getElementsByTagName("prerequisite"); for (var i=0;i<preReqList.length;i++){ preReqName=preReqList[i].getAttribute("name"); preReqType=preReqList[i].getAttribute("type"); if (gameVariables.index[preReqName]=='undefined'){ continue; } varGameValue=gameVariables.value[preReqName]; switch(preReqType){ case "n": { preReqValue1=(preReqList[i].getAttribute("minvalue")==null || parseInt(preReqList[i].getAttribute("minvalue"))=="")?0:parseInt(preReqList[i].getAttribute("minvalue")); preReqValue2=(parseInt(preReqList[i].getAttribute("maxvalue"))>=preReqValue1)?parseInt(preReqList[i].getAttribute("maxvalue")):preReqValue1; if (varGameValue>=preReqValue1 && varGameValue<=preReqValue2){ } else { tmpIndex=(preReqList[i].getAttribute("target")==null || preReqList[i].getAttribute("target")=="")?gameScene.current["index"]:xmlSceneID[preReqList[i].getAttribute("target")]; return tmpIndex; } break; } case "b": case "l": { preReqValue1=(preReqList[i].getAttribute("value")=="false")?false:true; if (varGameValue==preReqValue1){ } else { tmpIndex=(preReqList[i].getAttribute("target")==null || preReqList[i].getAttribute("target")=="")?gameScene.current["index"]:xmlSceneID[preReqList[i].getAttribute("target")]; return tmpIndex; } break; } default: break; } } preChkFlag["index"][idxTarget]=false; preChkFlag["name"][sTarget]=false; return idxTarget; } return idxTarget; } function updateUserAction(actString){ var usrActions=actString.split(":"); var usrActDefaultFlag; var usrAction, usrActEvent, usrActObj, usrActValue; for (var i=0;i<usrActions.length;i++){ usrActDefaultFlag=false; usrAction=usrActions[i].split("."); if (usrAction.length<2){ continue; } usrActEvent=usrAction[0]; usrActObj=usrAction[1].split(","); (usrAction.length==2)?usrActDefaultFlag=true:false; for (var j=0;j<usrActObj.length;j++){ switch(gameVariables.type[usrActObj[j]]){ case "n": { usrActValue=usrActDefaultFlag?1:parseInt(usrAction[2]); break; } case "b": case "l": { usrActValue=usrActDefaultFlag?false:((usrAction[2]=="true")?true:false); break; } default : { usrActValue=usrAction[2]; break; } } switch(usrActEvent){ case "add": case "inc": case "increase": gameVariables.value[usrActObj[j]]+=usrActValue; break; case "sub": case "dec": case "subtract": case "decrease": gameVariables.value[usrActObj[j]]-=usrActValue; break; case "multiply": gameVariables.value[usrActObj[j]]=gameVariables.value[usrActObj[j]]*usrActValue; break; case "divide": gameVariables.value[usrActObj[j]]=Math.round(gameVariables.value[usrActObj[j]]/usrActValue); break; case "buy": case "purchase": case "enable": case "unlock": gameVariables.value[usrActObj[j]]=true; break; case "lock": case "disable": gameVariables.value[usrActObj[j]]=false; break; case "set": gameVariables.value[usrActObj[j]]=usrActValue; break; default : // Reserved for future... break; } } } } function initGame(xDoc, xPath){ var confVarName, confVarType, confVarCat, confVarFlag; var confNodes=getXMLElements(xDoc, xPath); var confVariables=new Object(); confVariables.name=new Object(); confVariables.type=new Object(); confVariables.index=new Object(); confVariables.value=new Object(); confVariables.category=new Object(); for (var i=0;i<confNodes.length;i++){ confVarName=confNodes[i].getAttribute("name"); confVarCat=confNodes[i].getAttribute("category"); confVarType=confNodes[i].getAttribute("type"); confVariables.name[i]=confVarName; confVariables.type[confVarName]=confVarType; confVariables.index[confVarName]=i; confVariables.category[confVarName]=confVarCat; confVarFlag=(confNodes[i].getAttribute("default")==null || confNodes[i].getAttribute("default") == "")?false:confNodes[i].getAttribute("default"); switch(confVarType){ case "n": confVariables.value[confVarName]=(confVarFlag)?parseInt(confVarFlag):0; break; case "b": case "l": confVariables.value[confVarName]=(confVarFlag)?true:false; break; default: break; } } return confVariables; } function CvtStrToXml(){ var ToBeXml; var xmlString = '<?xml version="1.0" encoding="UTF-8"?><main><conf><variables><var type="n" name="mood" category="state" default="10" /><var type="n" name="vigor" category="state" default="" /><var type="n" name="drunk" category="state" /><var type="b" name="rose" category="item" /><var type="b" name="dress" category="item" /><var type="b" name="key" category="item" /><var type="l" name="bar1" category="scene" default="" /><var type="l" name="bar2" category="scene" default="true" /><var type="l" name="street1a" category="scene" /></variables></conf><data><scene category="" ><id value="street1" /><dialog>场景一 -(登场)看我性感就快行动吧!</dialog><image><name value="http://www.chinawwe.net/uploads/allimg/090904/23-0ZZ4222142.jpg" /><hotspots><hotspot target="street1a" value="10,10,200,260" shape="rect" tip="点击就会心情和活力大幅度变好,进入场景二" action="add.mood,vigor.10" /></hotspots></image><choices><choice type="" target="street1a" action="enable.key" value="确定想拿入场卷?去场景二瞧瞧" /><choice type="" target="bar1" action="lock.street1a" value="场景二要关门了,去场景三看看吧!" /></choices><prerequisites></prerequisites></scene><scene category="" ><id value="street1a" /><dialog>场景二 - Lilian Garcia:WWE冠军是......</dialog><image><name value="http://www.chinawwe.net/uploads/allimg/091009/23-09100Z04F0.jpg" /><hotspots><hotspot target="bar1" value="110,260,310,360" shape="rect" tip="心情略有转好,去场景三吧" action="add.mood" /><hotspot target="bar2" value="380,200,140" shape="circ" tip="来点酒喝吧!顺便去场景四" action="add.drunk.12" /></hotspots></image><choices><choice type="btn" target="bar1" action="set.rose" value="检到一支玫瑰,去场景三" /><choice type="btn" target="bar2" action="set.dress.false" value="衣服没了,去场景四找找!" /></choices><prerequisites><prerequisite name="bar1" type="l" value="false" target="" /><prerequisite name="mood" type="n" minvalue="5" maxvalue="100" target="street1" /></prerequisites></scene><scene category="" ><id value="bar1" /><dialog>场景三 - Kane: 你想和我试试么?</dialog><image><name value="http://www.chinawwe.net/uploads/allimg/091205/1-0912051RQ1.jpg" /><hotspots></hotspots></image><choices><choice type="" target="street1" action="add.vigor" value="活力小幅度提升,去场景一" /><choice type="" target="street1a" action="unlock.street1a" value="解锁场景二,可以去场景二了吧?" /></choices><prerequisites><prerequisite name="key" type="b" value="" target="bar2" /></prerequisites></scene><scene category="" ><id value="bar2" /><dialog>场景四 - (看到这图片,大家失望了吧:)</dialog><image><name value="http://www.baidu.com/img/baidu_logo.gif" /><hotspots><hotspot target="bar1" value="0,0,110,260" shape="rect" tip="我想去场景三!" action="" /><hotspot target="street1a" value="180,300,14" shape="circ" tip="听说场景二关了,想试试能去场景二么?" action="lock.street1a" /></hotspots></image><choices><choice type="" target="street1" action="dec.mood.4" value="心情有一定下降啊,还是回场景一吧!" /><choice type="btn" target="street1a" action="set.bar1.true" value="场景三已经开启,先去场景二" /></choices><prerequisites><prerequisite name="" type="" value="" target="" /><prerequisite name="" type="" value="" /></prerequisites></scene></data></main>'; // Mozilla and Netscape browsers if (document.implementation.createDocument) { var parser = new DOMParser() ToBeXml = parser.parseFromString(xmlString, "application/xml") // MSIE } else if (window.ActiveXObject) { ToBeXml = new ActiveXObject("Microsoft.XMLDOM") ToBeXml.async="false" ToBeXml.loadXML(xmlString) } return ToBeXml } </script> <style> * { padding: 0px auto; margin: 0px auto; } body { width: 100%; height: 100%; background: black; border: none; } table { width: 100%; height: 100%; border: 1px solid gray; padding: 0px 0px; margin: 0px 0px; } tr { width: 100%; } td { color: red; font-size: 10pt; font-weight: bold; text-align: center; valign: middle; border: 1px solid gray;} img { height: 100%; border: none; margin-right: auto; margin-left: auto; -ms-interpolation-mode: bicubic;} button { cursor: pointer; } div#main { width: 100%; height: 100%; position: absolute; top: 0px; bottom: 0px; } div#divDialog { width: 100%; height: 12%; color: orange; font-size: 12pt; font-weight: bold; text-align: center; position: relative; top: 0px; padding: 16px auto; } div#divMain { position:relative; top: 0px; bottom: 0px; text-align: center; margin-right: auto; margin-left: auto; width: 100%; height: 72% } div#divChoice { width: 100%; height: 10%; color: dodgerblue; font-size: 10pt; font-weight: bold; text-align: center; position: relative; bottom: 0px; padding: 0px 10px; } .curPointer { cursor: pointer; } </style> </head> <body> <div id="main"> <div id="divDialog">BETA版游戏<br/>说明:框架测试BETA版 by <a style="color:red;font-weight:bold;text-decoration:none;" href="mailto:[email protected]">暗翼飘雪</a>@BlueIdea</div> <div id="divMain"><img src="http://www.chinawwe.net/uploads/allimg/090919/23-0Z919203538.jpg" /></div> <div id="divChoice"><br/><label>输入玩家姓名: </label><input id="entName" value="" type="text" length=30 />&nbsp;&nbsp;<input style="color:red; font-weight:bold;" type="button" onclick="temp();" value="Play Game!"></div> </div> </body> <script language="javascript"> var n=0; var img, str, test; var expA='//main/data/scene['+n+']/image[0]/hotspots[0]/hotspot'; var expB='//main/data/scene['+n+']/image'; var expC='//main/data/scene['+n+']'; var exp='//main/data/scene'; var cfg='//main/conf/variables/var'; var xmlGameDoc=CvtStrToXml(); gameVariables=initGame(xmlGameDoc, cfg); var xmlNodes=getXMLElements(xmlGameDoc, exp); xmlSceneID=mapSceneID(xmlNodes); function temp(){ var ck=document.getElementById("entName"); if (ck.value == "" || ck.value == null) { alert("请输入玩家姓名\n比如:空格、1、塞纳、HHH等等!"); ck.style.cssText="border: 2px solid red; background: pink;"; ck.focus(); return false; } if(ck.value=="1" || ck.value=="2" || ck.value=="3" || ck.value=="0" || ck.value=="") { n=ck.value; } else { n=0; } document.getElementById("divDialog").innerHTML=xmlNodes[n].getElementsByTagName("dialog")[0].childNodes[0].nodeValue; document.getElementById("divMain").innerHTML=dispImage(xmlNodes[n].getElementsByTagName('image')[0].getElementsByTagName('name')[0].getAttribute("value"), xmlNodes[n].getElementsByTagName("hotspot")); document.getElementById("divChoice").innerHTML=dispChoice(xmlNodes[n].getElementsByTagName("choice")); } </script> </html>
 提示:您可以先修改部分代码再运行
应该会有某些BUG, 目前游戏输入的姓名没有加如到JS做相关显示.
谁有兴趣可以用这个框架写个小游戏或剧情玩玩
当然也能实现图片的上下浏览, 或者是 在线的类似测试IQ, EQ的测试, 从题目N选项X 跳到题目Y, 你所需要的就是在 XML文件里设置你的场景流向和前提条件等就可以完成.

最近没时间继续搞了, 以后有时间再完善这个, 搞个小游戏看看, 可能是VDG风格哦

由于最近忙,没时间继续搞下去, 暂时发布个BETA的游戏框架, 谁有兴趣就自己拿去用吧, 如果有什么改进记的EMAIL或PM我下,谢谢

以下是打包的, 打包的是 调用XML文件的, 上面的在线样板只是xmlString转xmlDOC的伪调用.
希望谁有兴趣的话把这个发展的强大一点, 顺便搞个什么好的样子出来!
template.rar (5.63 KB)
template.rar (5.63 KB)
下载次数: 3
2010-5-22 00:54

作者: 暗翼飘雪   发布时间: 2010-05-22

PS:
某些图片有设置热点, 可能1个, 可能2个, 或者米有, 鼠标HOVER后会有提示
测试的样板中, 只要满足特定条件才能看到场景三哦

[ 本帖最后由 暗翼飘雪 于 2010-5-22 00:57 编辑 ]

作者: 暗翼飘雪   发布时间: 2010-05-22