zoukankan      html  css  js  c++  java
  • 【CSON原创】HTML5游戏框架cnGameJS开发实录(精灵对象篇)

    返回目录

    1.什么是精灵对象(sprite)?

      所谓的精灵对象,就是游戏中的一个具有行为的元素,以超级玛丽为例,玛丽,敌人都算是一个精灵对象。在cnGameJS框架中,精灵对象如下几个特点:

      1.添加动画:在之前的动画篇中,我们介绍过cnGameJS如何实现帧动画。而作为精灵对象,就是动画的使用者。例如我们控制玛丽向不同方向的行走,玛丽会产生行走的动画。

      2.包含图像:对于另外一些精灵对象,它可能不需要运动动画,这时我们就可以只让它使用图像。

      3.能进行不同类型的运动:可以让精灵对象向不同方向,以不同加速度进行移动。

    2.demo展现

      这里以一个简单的demo进行展现,我们通过鼠标控制玛丽的行动(匀加速运动),当玛丽停止时,使用图片。当玛丽移动时,使用动画,键盘左右方向键控制玛丽的移动

      效果:

     

      代码:

    <body>
    <div><canvas id="gameCanvas">请使用支持canvas的浏览器查看</canvas></div>
    </body>
    <script src="https://files.cnblogs.com/Cson/cnGame_v1.0.js"></script>
    <script>
    var Src="http://images.cnblogs.com/cnblogs_com/Cson/290336/o_player.png";

    /* 初始化 */
    cnGame.init(
    'gameCanvas',{300,height:150});
    var floorY=cnGame.height-40;
    var gameObj=(function(){
    /* 玩家对象 */
    var player=function(options){
    this.init(options);
    this.speedX=0;
    this.moveDir;
    this.isJump=false;
    }
    cnGame.core.inherit(player,cnGame.Sprite);
    player.prototype.initialize
    =function(){
    this.addAnimation(new cnGame.SpriteSheet("playerRight",Src,{frameSize:[50,60],loop:true,150,height:60}));
    this.addAnimation(new cnGame.SpriteSheet("playerLeft",Src,{frameSize:[50,60],loop:true,150,height:120,beginY:60}));
    }
    player.prototype.moveRight
    =function(){
    if(cnGame.core.isUndefined(this.moveDir)||this.moveDir!="right"){
    this.moveDir="right";
    this.speedX<0&&(this.speedX=0);
    this.setMovement({aX:10,maxSpeedX:15});
    this.setCurrentAnimation("playerRight");
    }
    }
    player.prototype.moveLeft
    =function(){
    if(cnGame.core.isUndefined(this.moveDir)||this.moveDir!="left"){
    this.moveDir="left";
    this.speedX>0&&(this.speedX=0);
    this.setMovement({aX:-10,maxSpeedX:15});
    this.setCurrentAnimation("playerLeft");
    }
    }
    player.prototype.stopMove
    =function(){

    if(this.speedX<0){
    this.setCurrentImage(Src,0,60);
    }
    else if(this.speedX>0){
    this.setCurrentImage(Src);
    }
    this.moveDir=undefined;
    this.resetMovement();

    }
    player.prototype.update
    =function(){
    player.prototype.parent.prototype.update.call(
    this);//调用父类update
    if(cnGame.input.isPressed("right")){
    this.moveRight();
    }
    else if(cnGame.input.isPressed("left")){
    this.moveLeft();
    }
    else{
    this.stopMove();
    }


    }

    return {
    initialize:
    function(){
    cnGame.input.preventDefault([
    "left","right","up","down"]);
    this.player=new player({src:Src,50,height:60,x:0,y:floorY-60});
    this.player.initialize();
    },
    update:
    function(){
    this.player.update();
    },
    draw:
    function(){
    this.player.draw();
    }

    };
    })();
    cnGame.loader.start([Src],gameObj);
    </script>


    3.实现

      和动画篇spriteSheet对象一样,sprite对象同样划分三个阶段:初始化,更新,绘制

      首先看sprite的初始化函数:

            /**
    *初始化
    *
    */
    init:function(options){

    /**
    *默认对象
    *
    */
    var defaultObj={
    x:0,
    y:0,
    imgX:0,
    imgY:0,
    32,
    height:32,
    angle:0,
    speedX:0,
    speedY:0,
    aX:0,
    aY:0,
    maxSpeedX:postive_infinity,
    maxSpeedY:postive_infinity,
    maxX:postive_infinity,
    maxY:postive_infinity,
    minX:-postive_infinity,
    minY:-postive_infinity
    };
    options=options||{};
    options=cg.core.extend(defaultObj,options);
    this.x=options.x;
    this.y=options.y;
    this.angle=options.angle;
    this.width=options.width;
    this.height=options.height;
    this.angle=options.angle;
    this.speedX=options.speedX;
    this.speedY=options.speedY;
    this.aX=options.aX;
    this.aY=options.aY;
    this.maxSpeedX=options.maxSpeedX;
    this.maxSpeedY=options.maxSpeedY;
    this.maxX=options.maxX;
    this.maxY=options.maxY;
    this.minX=options.minX;
    this.minY=options.minY;


    this.spriteSheetList={};
    if(options.src){ //传入图片路径
    this.setCurrentImage(options.src,options.imgX,options.imgY);
    }
    else if(options.spriteSheet){//传入spriteSheet对象
    this.addAnimation(options.spriteSheet);
    setCurrentAnimation(options.spriteSheet);
    }

    }

      参数很多,主要包括:对象位置,旋转角度,尺寸,xy方向的速度,xy方向的加速度,xy方向的最大速度。另外如果用户传入图片地址,则设置当前sprite对象使用图片,否则使用spriteSheet动画。

      先看看sprite对象如何使用图像:

            /**
    *设置当前显示图像
    *
    */
    setCurrentImage:function(src,imgX,imgY){
    if(!this.isCurrentImage(src,imgX,imgY)){
    imgX=imgX||0;
    imgY=imgY||0;
    this.image=cg.loader.loadedImgs[src];
    this.imgX=imgX;
    this.imgY=imgY;
    this.spriteSheet=undefined;
    }
    },

      首先检测现在是否正在使用该图像,如果不是,则从loader里获取下载好的image对象(所有图像资源在游戏开始时已下载好,详情请看:HTML5游戏框架cnGameJS开发实录(资源加载模块篇)),并且设置spriteSheet为undefined(表示不使用spriteSheet动画),这样sprite对象就可以使用图像了。

      再看看如何使用动画:

            /**
    *设置当前显示动画
    *
    */
    setCurrentAnimation:function(id){//可传入id或spriteSheet
    if(!this.isCurrentAnimation(id)){
    if(cg.core.isString(id)){
    this.spriteSheet=this.spriteSheetList[id];
    this.image=this.imgX=this.imgY=undefined;
    }
    else if(cg.core.isObject(id)){
    this.spriteSheet=id;
    this.addAnimation(id);
    this.image=this.imgX=this.imgY=undefined;
    }
    }

    },

      首先根据传入的spriteSheet或spriteSheet的id判断是否正在使用该动画,如果不是,则设置sprite使用spriteSheet动画。

      设置好sprite对象使用动画后,核心函数update就负责调用spriteSheet的update,更新sprite使用的动画,需要注意的是使spriteSheet的xy为sprite的xy:

                
    if(this.spriteSheet){//更新spriteSheet动画
    this.spriteSheet.x=this.x
    this.spriteSheet.y=this.y;
    this.spriteSheet.update();
    }

      这样就完成的sprite对象动画的展示。

      最后看看如何实现最后一个特点:使sprite能进行变速的运动。

      要进行变速运动,我们需要确立如下几个变量:初始速度,经过的时间,以及加速度。现在看cnGameJS负责变速运动的部分:

            /**
    *设置移动参数
    *
    */
    setMovement:function(options){
    isUndefined=cg.core.isUndefined;
    isUndefined(options.speedX)?this.speedX=this.speedX:this.speedX=options.speedX;
    isUndefined(options.speedY)?this.speedY=this.speedY:this.speedY=options.speedY;

    isUndefined(options.aX)?this.aX=this.aX:this.aX=options.aX;
    isUndefined(options.aY)?this.aY=this.aY:this.aY=options.aY;
    isUndefined(options.maxX)?this.maxX=this.maxX:this.maxX=options.maxX;
    isUndefined(options.maxY)?this.maxY=this.maxY:this.maxY=options.maxY;
    isUndefined(options.minX)?this.minX=this.minX:this.minX=options.minX;
    isUndefined(options.minY)?this.minY=this.minY:this.minY=options.minY;


    if(this.aX!=0){
    this.startTimeX=new Date().getTime();
    this.oriSpeedX=this.speedX;
    isUndefined(options.maxSpeedX)?this.maxSpeedX=this.maxSpeedX:this.maxSpeedX=options.maxSpeedX;
    }
    if(this.aY!=0){
    this.startTimeY=new Date().getTime();
    this.oriSpeedY=this.speedY;
    isUndefined(options.maxSpeedY)?this.maxSpeedY=this.maxSpeedY:this.maxSpeedY=options.maxSpeedY;
    }

    }

      每次用户调用setMovement,就保留sprite的初速度,和运动开始的时间。这样在每次update的时候,就可以根据前面两个变量,获取到sprite现时的速度,并计算现时的XY方向上的位移:

     

                if(this.aX!=0){
    var now=new Date().getTime();
    var durationX=now-this.startTimeX;
    var speedX=this.oriSpeedX+this.aX*durationX/1000;
    if(this.maxSpeedX<0){
    this.maxSpeedX*=-1;
    }
    if(speedX<0){
    this.speedX=Math.max(speedX,this.maxSpeedX*-1) ;
    }
    else{
    this.speedX=Math.min(speedX,this.maxSpeedX);
    }
    }
    if(this.aY!=0){
    var now=new Date().getTime();
    var durationY=now-this.startTimeY;
    this.speedY=this.oriSpeedY+this.aY*durationY/1000;
    }
    this.move(this.speedX,this.speedY);

      当update更新了sprite的位移,就可以通过第三个阶段draw方法,把sprite绘制出来。

            /**
    *绘制出sprite
    *
    */
    draw:function(){
    var context=cg.context;
    if(this.spriteSheet){
    this.spriteSheet.x=this.x
    this.spriteSheet.y=this.y;
    this.spriteSheet.draw();
    }
    else if(this.image){
    context.save()
    context.translate(this.x, this.y);
    context.rotate(this.angle * Math.PI / 180);
    context.drawImage(this.image,this.imgX,this.imgY,this.width,this.height,0,0,this.width,this.height);
    context.restore();
    }

    },

      注意sprite在使用spriteSheet动画或在使用图像,draw方法执行都不相同。当使用sprieSheet动画时,draw方法实质上是调用了spriteSheet的draw方法绘制帧图像,而当sprite使用图像,我们还可以让图像旋转之后再绘制。

      sprite对象还提供一个getRect方法,返回包含该sprite对象的矩形。该方法为检测sprite对象和其他对象的碰撞带来方便。有关碰撞检测请看:HTML5游戏框架cnGameJS开发实录(碰撞检测篇)

      另外sprite对象同样具有move,moveTo,resize,resizeTo等功能,方便对该对象的位置和尺寸进行操控。

      sprite对象所有源码:

    /**
    *
    *sprite对象
    *
    *
    */
    cnGame.register("cnGame",function(cg){

    var postive_infinity=Number.POSITIVE_INFINITY;

    var sprite=function(id,options){
    if(!(this instanceof arguments.callee)){
    return new arguments.callee(id,options);
    }
    this.init(id,options);
    }
    sprite.prototype={
    /**
    *初始化
    *
    */
    init:function(options){

    /**
    *默认对象
    *
    */
    var defaultObj={
    x:0,
    y:0,
    imgX:0,
    imgY:0,
    32,
    height:32,
    angle:0,
    speedX:0,
    speedY:0,
    aX:0,
    aY:0,
    maxSpeedX:postive_infinity,
    maxSpeedY:postive_infinity,
    maxX:postive_infinity,
    maxY:postive_infinity,
    minX:-postive_infinity,
    minY:-postive_infinity
    };
    options=options||{};
    options=cg.core.extend(defaultObj,options);
    this.x=options.x;
    this.y=options.y;
    this.angle=options.angle;
    this.width=options.width;
    this.height=options.height;
    this.angle=options.angle;
    this.speedX=options.speedX;
    this.speedY=options.speedY;
    this.aX=options.aX;
    this.aY=options.aY;
    this.maxSpeedX=options.maxSpeedX;
    this.maxSpeedY=options.maxSpeedY;
    this.maxX=options.maxX;
    this.maxY=options.maxY;
    this.minX=options.minX;
    this.minY=options.minY;

    this.spriteSheetList={};
    if(options.src){ //传入图片路径
    this.setCurrentImage(options.src,options.imgX,options.imgY);
    }
    else if(options.spriteSheet){//传入spriteSheet对象
    this.addAnimation(options.spriteSheet);
    setCurrentAnimation(options.spriteSheet);
    }

    },
    /**
    *返回包含该sprite的矩形对象
    *
    */
    getRect:function(){
    return new cg.shape.Rect({x:this.x,y:this.y,this.width,height:this.height});

    },
    /**
    *添加动画
    *
    */
    addAnimation:function(spriteSheet){
    this.spriteSheetList[spriteSheet.id]=spriteSheet;
    },
    /**
    *设置当前显示动画
    *
    */
    setCurrentAnimation:function(id){//可传入id或spriteSheet
    if(!this.isCurrentAnimation(id)){
    if(cg.core.isString(id)){
    this.spriteSheet=this.spriteSheetList[id];
    this.image=this.imgX=this.imgY=undefined;
    }
    else if(cg.core.isObject(id)){
    this.spriteSheet=id;
    this.addAnimation(id);
    this.image=this.imgX=this.imgY=undefined;
    }
    }

    },
    /**
    *判断当前动画是否为该id的动画
    *
    */
    isCurrentAnimation:function(id){
    if(cg.core.isString(id)){
    return (this.spriteSheet&&this.spriteSheet.id===id);
    }
    else if(cg.core.isObject(id)){
    return this.spriteSheet===id;
    }
    },
    /**
    *设置当前显示图像
    *
    */
    setCurrentImage:function(src,imgX,imgY){
    if(!this.isCurrentImage(src,imgX,imgY)){
    imgX=imgX||0;
    imgY=imgY||0;
    this.image=cg.loader.loadedImgs[src];
    this.imgX=imgX;
    this.imgY=imgY;
    this.spriteSheet=undefined;
    }
    },
    /**
    *判断当前图像是否为该src的图像
    *
    */
    isCurrentImage:function(src,imgX,imgY){
    imgX=imgX||0;
    imgY=imgY||0;
    var image=this.image;
    if(cg.core.isString(src)){
    return (image&&image.srcPath===src&&this.imgX===imgX&&this.imgY===imgY);
    }
    },
    /**
    *设置移动参数
    *
    */
    setMovement:function(options){
    isUndefined=cg.core.isUndefined;
    isUndefined(options.speedX)?this.speedX=this.speedX:this.speedX=options.speedX;
    isUndefined(options.speedY)?this.speedY=this.speedY:this.speedY=options.speedY;

    isUndefined(options.aX)?this.aX=this.aX:this.aX=options.aX;
    isUndefined(options.aY)?this.aY=this.aY:this.aY=options.aY;
    isUndefined(options.maxX)?this.maxX=this.maxX:this.maxX=options.maxX;
    isUndefined(options.maxY)?this.maxY=this.maxY:this.maxY=options.maxY;
    isUndefined(options.minX)?this.minX=this.minX:this.minX=options.minX;
    isUndefined(options.minY)?this.minY=this.minY:this.minY=options.minY;


    if(this.aX!=0){
    this.startTimeX=new Date().getTime();
    this.oriSpeedX=this.speedX;
    isUndefined(options.maxSpeedX)?this.maxSpeedX=this.maxSpeedX:this.maxSpeedX=options.maxSpeedX;
    }
    if(this.aY!=0){
    this.startTimeY=new Date().getTime();
    this.oriSpeedY=this.speedY;
    isUndefined(options.maxSpeedY)?this.maxSpeedY=this.maxSpeedY:this.maxSpeedY=options.maxSpeedY;
    }

    },
    /**
    *重置移动参数回到初始值
    *
    */
    resetMovement:function(){
    this.speedX=0;
    this.speedY=0;
    this.aX=0;
    this.aY=0;
    this.maxSpeedX=postive_infinity;
    this.maxSpeedY=postive_infinity;
    this.maxX=postive_infinity;
    this.minX=-postive_infinity;
    this.maxY=postive_infinity;
    this.minY=-postive_infinity;
    },
    /**
    *更新位置和帧动画
    *
    */
    update:function(){
    if(this.aX!=0){
    var now=new Date().getTime();
    var durationX=now-this.startTimeX;
    var speedX=this.oriSpeedX+this.aX*durationX/1000;
    if(this.maxSpeedX<0){
    this.maxSpeedX*=-1;
    }
    if(speedX<0){
    this.speedX=Math.max(speedX,this.maxSpeedX*-1) ;
    }
    else{
    this.speedX=Math.min(speedX,this.maxSpeedX);
    }
    }
    if(this.aY!=0){
    var now=new Date().getTime();
    var durationY=now-this.startTimeY;
    this.speedY=this.oriSpeedY+this.aY*durationY/1000;
    }
    this.move(this.speedX,this.speedY);


    if(this.spriteSheet){//更新spriteSheet动画
    this.spriteSheet.x=this.x
    this.spriteSheet.y=this.y;
    this.spriteSheet.update();
    }
    },
    /**
    *绘制出sprite
    *
    */
    draw:function(){
    var context=cg.context;
    if(this.spriteSheet){
    this.spriteSheet.x=this.x
    this.spriteSheet.y=this.y;
    this.spriteSheet.draw();
    }
    else if(this.image){
    context.save()
    context.translate(this.x, this.y);
    context.rotate(this.angle * Math.PI / 180);
    context.drawImage(this.image,this.imgX,this.imgY,this.width,this.height,0,0,this.width,this.height);
    context.restore();
    }

    },
    /**
    *移动一定距离
    *
    */
    move:function(dx,dy){
    dx=dx||0;
    dy=dy||0;
    var x=this.x+dx;
    var y=this.y+dy;
    this.x=Math.min(Math.max(this.minX,x),this.maxX);
    this.y=Math.min(Math.max(this.minY,y),this.maxY);
    return this;

    },
    /**
    *移动到某处
    *
    */
    moveTo:function(x,y){
    this.x=Math.min(Math.max(this.minX,x),this.maxX);
    this.y=Math.min(Math.max(this.minY,y),this.maxY);
    return this;
    },
    /**
    *旋转一定角度
    *
    */
    rotate:function(da){
    this.angle+=da;
    return this;
    },
    /**
    *旋转到一定角度
    *
    */
    rotateTo:function(){
    this.angle=da;
    return this;

    },
    /**
    *改变一定尺寸
    *
    */
    resize:function(dw,dh){
    this.width+=dw;
    this.height+=dh;
    return this;
    },
    /**
    *改变到一定尺寸
    *
    */
    resizeTo:function(width,height){
    this.width=width;
    this.height=height;
    return this;
    }

    }
    this.Sprite=sprite;

    });
  • 相关阅读:
    函数function
    文件操作
    手机抓包app在python中使用
    手机app抓包工具,安卓手机adb无线连接
    selenium+options配置文件
    scrapy 执行同个项目多个爬虫
    最简单的???ubuntu 通过crontab定时执行一个程序
    scrapycrawl 爬取笔趣阁小说
    python装饰器见解笔记
    有关于python内置函数exec和eval一些见解笔记
  • 原文地址:https://www.cnblogs.com/Cson/p/2349661.html
Copyright © 2011-2022 走看看