in this tutorial, i will try to explain how i wrote the svg version of qbert. we will go through the process step by step.
in this first part i will cover the creation of the gameboard and the animated moves.
first we put the block field created in the last example and put it in a defs section. additionaly we insert a group for the gameboard(id="gb").
<defs>
<g id="wuerfel">
...
</g>
</defs>
<g id="gb" transform="translate(400 100)"/>
now we add a script that copies the group("wuerfel") and places these copies to the right position.
var SVGboard=new Array(36) //an array holding references to the svg represetations of the gameboards fields.
function createGame(){
for(var i=0;i<=7;i++){
for(var j=0;j<=(7-i);j++){
p=i*8+j
var n=document.getElementById("wuerfel").cloneNode(true)
n.getElementsByTagName("path").item(4).setAttribute("id","ld_"+p)
n.getElementsByTagName("path").item(3).setAttribute("id","rd_"+p)
n.getElementsByTagName("path").item(5).setAttribute("id","ru_"+p)
n.getElementsByTagName("path").item(6).setAttribute("id","lu_"+p)
var x=i*50-(j*50)
var y=i*75+(j*75)
n.setAttribute("transform","translate("+x+","+y+")")
SVGboard[p]=n
document.getElementById("gb").appendChild(n)
}
}
}
note! we add a new id for each copied path, so we can later reference them.
click here to see the svg file.
what we need next is a character that can move around.
for that we create a template ("bert_temp") , and the character that is actually moving around ("bert").
i also changed the structure of the file a bit , so one can move around the gameboard with a single animateMotion.
<defs>
<radialGradient id="grd_12" cx="40%" cy="40%" fx="30%" fy="30%">
<stop offset="0%" stop-color="red"/>
<stop offset="100%" stop-color="black"/>
</radialGradient>
<g id="bert_temp">
<circle cx="0" cy="0" r="25" fill="url(#grd_12)"/>
</g>
<g id="wuerfel">
...
</g>
</defs>
<g transform="translate(400 100)">
<g id="gb"/>
<g id="bert" pointer-events="none" fill-opacity="1" transform="translate(0 -25)">
<use xlink:href="#bert_temp"/>
<animateMotion id="bmove" begin="indefinite" dur="0.4s" fill="freeze">
<mpath id="bm2" xlink:href="#tp"/>
</animateMotion>
</g>
</g>
now it gets a bit more complicated. first we declare a few variables.
var isAnimating=0 //flag that holds the state of animation
var GameState="play" //game state will be used later, for now only "play" exists
var position=0 // current position on the board
var bm2=document.getElementById("bm2") //reference to the mpath of berts animateMotion
var mAnim=document.getElementById("bmove")// bert animateMotion element
mAnim.addEventListener("end",changeColor,false)//the changeColor function will only be use to set isAnimating to 0 again
and add a few lines to the createGame() function. since the transformation we set in the function will not take effect on the pathes when they get referenced by animateMotion, we have to add
the transformation by hand, and we add an event listener for onclick to each field
function createGame(){
for(var i=0;i<=7;i++){
for(var j=0;j<=(7-i);j++){
p=i*8+j
var n=document.getElementById("wuerfel").cloneNode(true)
n.getElementsByTagName("path").item(4).setAttribute("id","ld_"+p)
n.getElementsByTagName("path").item(3).setAttribute("id","rd_"+p)
n.getElementsByTagName("path").item(5).setAttribute("id","ru_"+p)
n.getElementsByTagName("path").item(6).setAttribute("id","lu_"+p)
var x=i*50-(j*50)
var y=i*75+(j*75)
//*****************************************
//i could have also used relative pathes, so only the M parameter has got to be changed.
n.getElementsByTagName("path").item(4).setAttribute("d","M"+(x)+" "+(y-25)+"C"+(x)+" "+(y-100)+" "+(x-50)+" "+(y-75)+" "+(x-50)+" "+(y+75))
n.getElementsByTagName("path").item(3).setAttribute("d","M"+(x)+" "+(y-25)+"C"+(x)+" "+(y-100)+" "+(x+50)+" "+(y-75)+" "+(x+50)+" "+(y+75))
n.getElementsByTagName("path").item(5).setAttribute("d","M"+(x)+" "+(y-25)+"C"+(x)+" "+(y-125)+" "+(x-50)+" "+(y-175)+" "+(x-50)+" "+(y-75))
n.getElementsByTagName("path").item(6).setAttribute("d","M"+(x)+" "+(y-25)+"C"+(x)+" "+(y-125)+" "+(x+50)+" "+(y-175)+" "+(x+50)+" "+(y-75))
//*****************************************
n.setAttribute("onclick","selectMove("+p+")")
n.setAttribute("transform","translate("+x+","+y+")")
SVGboard[p]=n
document.getElementById("gb").appendChild(n)
}
}
}
this will, at first, look a bit strange, but the pathes are invisible so it doesnt matter.
in the createGame() function we just added
n.setAttribute("onclick","selectMove("+p+")") so we need a
selectMove() function.
this function checks if the move you selected is leagal, and if so executes it, by setting the apropriate path to the mPath element and then starting the animation.
position get incremented or decremented appropriatly ,and isAnimating is set to 1. as long as isAnimating is 1 no userinput will be alowed.
function selectMove(ind){
if(isAnimating==0){if(GameState=="play"){
if((position-8)==ind){
bm2.setAttribute("xlink:href","#ru_"+position)
position-=8
isAnimating=1
mAnim.beginElement()
}
if((position+8)==ind){
bm2.setAttribute("xlink:href","#rd_"+position)
position+=8
isAnimating=1
mAnim.beginElement()
}
if((position+1)==ind){if(ind!=8){
bm2.setAttribute("xlink:href","#ld_"+position)
position++
isAnimating=1
mAnim.beginElement()
}}
if((position-1)==ind){if(ind!=7){
bm2.setAttribute("xlink:href","#lu_"+position)
position--
isAnimating=1
mAnim.beginElement()
}}
}}
}
now only one function is missing . it is quite simple as mentioned above.
function changeColor(){
isAnimating=0
}
click here to see the svg file.
thats it for the first part, i hope it was informative.
please leave your comments at