• html5 Game开发系列文章之 二 精灵(下)


      昨天在家装了IE9,感觉不错,界面终于清爽了,跑HTML5也还不错!期待IE10正式版!

      上一节我简单了介绍了HTML5 canvas 中的 drawImage方法,并绘制了一条扇动翅膀的小红龙,那么,今天我想让小龙在我的控制下飞行起来!

      首先我们再回顾下drawImage的九个参数,第一个参数是绘制对象,可以是图片,也可以是canvas,第二到五个参数是图片的裁剪参数,控制裁剪的位置与大小,第六至九个参数控制绘制到画布的位置与大小!到这里,估计大家都知道该怎么做了,只要更改图片在画面的位置就可以让小红龙“飞行”起来了!

      不过先别急,在这处理这个过程前,还有涉及到一个方向的问题,在2D游戏中,一般主流有四个方向或者八个方向两种(横版的是二个方向的,最近也比较流行),我这里将以八个方向为例,演示如何控制小红龙的飞行!

      为了方便控制,我先定义一个枚举对象,嗯,不过JS中不存在枚举对象,没关系,JS是一种十分自由的语言,我发现我越来越喜欢它了,我定义一个对象字面量!

    var Directions={
    North:6,
    NorthEast:7,
    East:0,
    SouthEast:1,
    South:2,
    SouthWest:3,
    West:4,
    NorthWest:5
    };

      本节演示的精灵素材继续沿用上一节的小红龙!我在这里用0-7 来代表东南西北等八个方向!这里哪个方向代表哪个数字是有一定规律的,细心的朋友可能发现了,在素材图中,每一行均代表了一个方向!Directions中每个方向的值对应每行的索引值,比如第一行,红龙的朝向为东,则Directions.East=0,第二行为东南,则Directions.SouthEast=1,依此类推!

      然后定义一个对象来保存精灵的当前位置,一个对象来保存精灵当前朝向!

    var currentPoint = {"X":0,"Y":0};
    var direction=0;

      然后可以通过onclick事件来处理精灵的移动!另外,我通过canvas的onmousemove事件来获取当前的鼠标点击坐标,完整代码如下

    var ctx = document.getElementById("scene").getContext("2d");
    var ctxW = document.getElementById("scene").width;//画布宽
    var ctxH = document.getElementById("scene").height;//画面高
    var currentPoint = {"X":0,"Y":0};//精灵当前位置
    var mousePoint = {"X":0,"Y":0};//鼠标相对位置
    var direction=0;//当前朝向
    var Directions={
    North:6,
    NorthEast:7,
    East:0,
    SouthEast:1,
    South:2,
    SouthWest:3,
    West:4,
    NorthWest:5
    };

    var timer;//定时器
    //
    获取鼠标相对坐标
    document.getElementById("scene").onmousemove=function(e){
    e = e || window.event;
    if(e.pageX || e.pageY){
    mousePoint.X=parseInt(e.pageX);
    mousePoint.Y = parseInt(e.pageY);
    }else{
    mousePoint.X=parseInt(e.clientX + document.body.scrollLeft - document.body.clientLeft);
    mousePoint.Y=parseInt(e.clientY + document.body.scrollTop - document.body.clientTop);
    }
    var boundingClient=document.getElementById("scene").getBoundingClientRect();
    mousePoint.X-=parseInt(boundingClient.left+document.documentElement.scrollLeft);
    mousePoint.Y-=parseInt(boundingClient.top+document.documentElement.scrollTop);
    };
    //获取两个坐标点的距离
    function GetDistance(x,y){
    return Math.sqrt(Math.pow((x.X - y.X), 2) + Math.pow((x.Y - y.Y), 2));
    };
    //获取精灵朝向
    function GetDirection(current,target){
    var n = (target.Y - current.Y) / (target.X - current.X);
    if (Math.abs(n) >= Math.tan(Math.PI * 3 / 8) && target.Y <= current.Y) {
    return Directions.North;
    } else if (Math.abs(n) > Math.tan(Math.PI / 8) && Math.abs(n) < Math.tan(Math.PI * 3 / 8) && target.X > current.X && target.Y < current.Y) {
    return Directions.NorthEast;
    } else if (Math.abs(n) <= Math.tan(Math.PI / 8) && target.X >= current.X) {
    return Directions.East;
    } else if (Math.abs(n) > Math.tan(Math.PI / 8) && Math.abs(n) < Math.tan(Math.PI * 3 / 8) && target.X > current.X && target.Y > current.Y) {
    return Directions.SouthEast;
    } else if (Math.abs(n) >= Math.tan(Math.PI * 3 / 8) && target.Y >= current.Y) {
    return Directions.South;
    } else if (Math.abs(n) > Math.tan(Math.PI / 8) && Math.abs(n) < Math.tan(Math.PI * 3 / 8) && target.X < current.X && target.Y > current.Y) {
    return Directions.SouthWest;
    } else if (Math.abs(n) <= Math.tan(Math.PI / 8) && target.X <= current.X) {
    return Directions.West;
    } else if (Math.abs(n) > Math.tan(Math.PI / 8) && Math.abs(n) < Math.tan(Math.PI * 3 / 8) && target.X < current.X && target.Y < current.Y) {
    return Directions.NorthWest;
    } else {
    return 0;
    }
    };
    //判断精灵是否到达指定坐标
    function RatherPoint(p1,p2){
    switch(direction){
    case Directions.North:
    return p1.Y<=p2.Y;
    case Directions.NorthEast:
    return p1.X>=p2.X||p1.Y<=p2.Y;
    case Directions.East:
    return p1.X>=p2.X;
    case Directions.SouthEast:
    return p1.X>=p2.X||p1.Y>=p2.Y;
    case Directions.South:
    return p1.Y>=p2.Y;
    case Directions.SouthWest:
    return p1.X<=p2.X||p1.Y>=p2.Y;
    case Directions.West:
    return p1.X<=p2.X;
    case Directions.NorthWest:
    return p1.X<=p2.X || p1.Y<=p2.Y;
    };
    return true;
    };
    //获取每次移动步长
    function GetMovePoint(toPoint){
    var m=0,n=0,t=1;
    var speed=10;//移动速度
    switch(direction){
    case Directions.North:
    m=0;
    n=-speed;
    break;
    case Directions.NorthEast:
    t = GetDistance(currentPoint,toPoint);
    m=speed * (toPoint.X-currentPoint.X)/t;
    n=-speed * (currentPoint.Y - toPoint.Y)/t;
    break;
    case Directions.East:
    m=speed;
    n=0;
    break;
    case Directions.SouthEast://alert("Directions.SouthEast"+toPoint.X);
    t = GetDistance(currentPoint,toPoint);//alert(t);
    m=speed * (toPoint.X-currentPoint.X)/t;
    n=speed * (toPoint.Y-currentPoint.Y)/t;
    break;
    case Directions.South:
    m=0;
    n=speed;
    break;
    case Directions.SouthWest:
    t = GetDistance(currentPoint,toPoint);
    m=-speed * (currentPoint.X-toPoint.X)/t;
    n=speed * (toPoint.Y-currentPoint.Y)/t;
    break;
    case Directions.West:
    m=-speed;
    n=0;
    break;
    case Directions.NorthWest:
    t = GetDistance(currentPoint,toPoint);
    m=-speed * (currentPoint.X-toPoint.X)/t;
    n=-speed * (currentPoint.Y-toPoint.Y)/t;
    break;
    };
    return {"X":m,"Y":n};
    }
    //鼠标点击事件
    document.getElementById("scene").onclick=function(){
    clearInterval(timer);//清除定时器
    var tp = {"X":mousePoint.X,"Y":mousePoint.Y};//鼠标点击坐标,引用值不能直接用mousePoint,不然当鼠标在点击后移动时,目的坐标会变更
    direction = GetDirection(currentPoint,tp);//设置精灵朝向
    var movePoint = GetMovePoint(tp);//获取移动步长
    timer=setInterval(function(){//判断是否到达目标
    if(!RatherPoint(currentPoint,tp)){
    currentPoint.X+=movePoint.X;
    currentPoint.Y+=movePoint.Y;
    //碰撞检测
    if(currentPoint.X>ctxW){
    currentPoint=ctxW;
    clearInterval(timer);
    }
    if(currentPoint.X<0){
    currentPoint=0;
    clearInterval(timer);
    }
    if(currentPoint.Y>ctxH){
    currentPoint=ctxH;
    clearInterval(timer);
    }
    if(currentPoint.Y<0){
    currentPoint=0;
    clearInterval(timer);
    }
    }else{
    clearInterval(timer);
    }
    },100);
    }

    var index=0;
    var img = new Image();
    img.onload=function(){
    setInterval(function(){
    if(index>=9){
    index=0;
    }else{
    index++;
    }
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    ctx.drawImage(img,index * 75,direction * 70 ,75,70,currentPoint.X,currentPoint.Y,75,70);
    },50);
    }
    img.src="1.jpg";

      

      GetDistance, GetDirection这两个方法其实我是直接从深蓝的博客中的copy过来的,有兴趣的朋友可以去看下他写的Silverlight游戏开发系列教程,写得非常好,地址是:http://www.cnblogs.com/alamiye010

      其实HTML5除了安全性能上面外,有一些效果处理起来还是相当方便的!大家玩魔兽时,对山丘之王的天神下凡应该都有印象,小矮人大吼一声,身子迅速变大,威风凛凛,其实用HTML5来做的话,非常简单的,其实就是图片缩放效果而已!

      不废话,直接上代码:

    var ctx = document.getElementById("scene").getContext("2d");
    var index=0;
    var img = new Image();
    var size = 1;
    var timer;
    img.onload=function(){
    setInterval(function(){
    if(index>=9){
    index=0;
    }else{
    index++;
    }
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    ctx.drawImage(img,index * 75,0 ,75,70,20,20,parseInt(75*size),parseInt(70*size));
    },50);
    }
    function BieShen(){
    size = 1;
    timer=setInterval(function(){
    size+=0.1;//设定每次增大10%
    if(size>2){
    size=2;
    clearInterval(timer);
    }
    },50);
    }
    img.src="1.jpg";

      到这里,一个基本的精灵就已经出现了!不过如果用来做游戏的话,这样零散的代码是很痛苦的,下一节,我将介绍如果封装这些代码!

      本节代码源码下载地址https://files.cnblogs.com/hnvvv/html5_Game_L1_2.zip (注:变身效果也在里面,图片是直接以base64的字符串直接保存在HTML中,下载后直接用火狐,Chrome,IE9都可以运行)

      演示地址http://www.jiniannet.com/html5/ (注:里面的代码比文章里面的要新一些,已经完成了基本的精灵,地图等,会不定时修改,每篇文章的源代码直接在文章后附出,不单独出现在演示地址中)

      目录地址http://www.cnblogs.com/hnvvv/archive/2012/01/09/2317336.html

      

      欢迎大家回复你的意见,比如哪些东西需要详细介绍,哪些东西可以简写,代码有更好的实现方法等等,我会一一改进的!

        
    如无特殊说明,本博文章均属原创,转载请注明作者:翅膀的初衷 与出处: http://www.cnblogs.com/hnvvv
  • 相关阅读:
    牛客国庆集训派对Day6 Solution
    牛客国庆集训派对Day5 Solution
    牛客国庆集训派对Day4 Solution
    牛客国庆集训派对Day3 Solution
    牛客国庆集训派对Day2 Solution
    牛客国庆集训派对Day1 Solution
    The 2018 ACM-ICPC Asia Qingdao Regional Contest, Online Solution
    ACM-ICPC 2018 焦作赛区网络预赛 Solution
    2016 CCPC 长春 Solution
    CCPC 2017-2018, Finals Solution
  • 原文地址:https://www.cnblogs.com/hnvvv/p/2322218.html
Copyright © 2020-2023  润新知