0

0

HTML5游戏框架cnGameJS开发实录-精灵对象篇

黄舟

黄舟

发布时间:2017-03-25 15:05:53

|

1955人浏览过

|

来源于php中文网

原创

返回目录

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

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

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

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

立即学习前端免费学习笔记(深入)”;

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

2.demo展现

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

  效果:

 1281.png

  代码:


请使用支持canvas的浏览器查看
复制代码

3.实现

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

  首先看sprite的初始化函数

/**
         *初始化
        **/
        init:function(options){
            
            /**
             *默认对象
            **/    
            var defaultObj={
                x:0,
                y:0,
                imgX:0,
                imgY:0,
                width: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动画。

ClipDrop Relight
ClipDrop Relight

ClipDrop推出的AI图片图像打光工具

下载

  先看看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对象(所有图像资源在游戏开始时已下载好,),并且设置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,
                width: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,width: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;                              
                                  
});
复制代码

相关文章

HTML速学教程(入门课程)
HTML速学教程(入门课程)

HTML怎么学习?HTML怎么入门?HTML在哪学?HTML怎么学才快?不用担心,这里为大家提供了HTML速学教程(入门课程),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
Java 项目构建与依赖管理(Maven / Gradle)
Java 项目构建与依赖管理(Maven / Gradle)

本专题系统讲解 Java 项目构建与依赖管理的完整体系,重点覆盖 Maven 与 Gradle 的核心概念、项目生命周期、依赖冲突解决、多模块项目管理、构建加速与版本发布规范。通过真实项目结构示例,帮助学习者掌握 从零搭建、维护到发布 Java 工程的标准化流程,提升在实际团队开发中的工程能力与协作效率。

9

2026.01.12

c++主流开发框架汇总
c++主流开发框架汇总

本专题整合了c++开发框架推荐,阅读专题下面的文章了解更多详细内容。

102

2026.01.09

c++框架学习教程汇总
c++框架学习教程汇总

本专题整合了c++框架学习教程汇总,阅读专题下面的文章了解更多详细内容。

59

2026.01.09

学python好用的网站推荐
学python好用的网站推荐

本专题整合了python学习教程汇总,阅读专题下面的文章了解更多详细内容。

139

2026.01.09

学python网站汇总
学python网站汇总

本专题整合了学python网站汇总,阅读专题下面的文章了解更多详细内容。

13

2026.01.09

python学习网站
python学习网站

本专题整合了python学习相关推荐汇总,阅读专题下面的文章了解更多详细内容。

19

2026.01.09

俄罗斯手机浏览器地址汇总
俄罗斯手机浏览器地址汇总

汇总俄罗斯Yandex手机浏览器官方网址入口,涵盖国际版与俄语版,适配移动端访问,一键直达搜索、地图、新闻等核心服务。

90

2026.01.09

漫蛙稳定版地址大全
漫蛙稳定版地址大全

漫蛙稳定版地址大全汇总最新可用入口,包含漫蛙manwa漫画防走失官网链接,确保用户随时畅读海量正版漫画资源,建议收藏备用,避免因域名变动无法访问。

471

2026.01.09

php学习网站大全
php学习网站大全

精选多个优质PHP入门学习网站,涵盖教程、实战与文档,适合零基础到进阶开发者,助你高效掌握PHP编程。

49

2026.01.09

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
HTML5/CSS3/JavaScript/ES6入门课程
HTML5/CSS3/JavaScript/ES6入门课程

共102课时 | 6.6万人学习

HTML+CSS基础与实战
HTML+CSS基础与实战

共132课时 | 9.4万人学习

前端开发(基础+实战项目合集)
前端开发(基础+实战项目合集)

共60课时 | 3.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号