• 【HTML5 Canvas游戏开发】笔记(二) 显示一张图片


    本系列文章由Shin-Knight编写,转载需注明出处。

    作者:Shin-Knight

    邮箱:shinknight@163.com

    文章链接:http://www.cnblogs.com/knightls/p/3281387.html

    在笔记一中,我们对html5游戏开发做了一个总的概述,接下来就该轮到实现功能的时候了。在上一章中也提到过,图片是游戏中不可缺少的一部分,因此我们今天就先来实现一下。

    首先,我觉得可以先讲一讲本次开发的原理和设计方案:

    为了方便以后实现层次化效果,我们可以采用绘画的先后顺序来实现。但是如果说我们有两个层:A和B,如下图放置:

    假设我们先画了B层,然后再画了A层。接着,我们往B层上添加个C层,这时候,如果还是将C层直接画在界面上,显示是在A层上,而不是B层上,因此,我们需要不断地重画这个界面。将A层,B层,C层加到显示列表中,通过遍历这个列表进行显示。当然,这是以后涉及到层次化效果的时候要使用的,现在讲讲只是为了理解本文中,我采取重绘界面做法的意义。

    由于本次开发涉及到封装,所以暂时给这个项目取名为Tomato2D吧,哈哈。 名字是随便想的,以后或许还会改。

    一,TGlobalVar 、TMainWindow和TApplication类

    有一些全局的变量和函数为了使用起来不重复,因此定义了一个TGlobalVar静态类,它负责装一些全局变量和函数,以免使用的时候和其他的变量相同。如下:

    1 var TGlobalVar = function(){this.type = "TGlobalVar";};

    这个以后如果有其他变量加入的话,会慢慢拓展。接下来为了使界面结构更清晰我们还要建立一个TMainWindow和TApplication类。负责全局的驱动和存储一些全局数据。

    先看TMainWindow的代码:

     1 function TMainWindow(data){
     2     var self = this;
     3     
     4     self.type = "TMainWindow";
     5     
     6     self.childList = new Array();
     7     self.width = data.width || 200;
     8     self.height = data.height || 120;
     9     TGlobalVar.TApplicationObj.id = data.id;
    10     TGlobalVar.TApplicationObj.frameRate = data.frameRate;
    11 }
    12 TMainWindow.prototype.init = function(){
    13     var self = this;
    14     
    15     if(TGlobalVar.isAddMainWindow == true)return;
    16     TGlobalVar.TMainWindowObj = self;
    17     TGlobalVar.isAddMainWindow = true;
    18     var w = self.width;
    19     var h = self.height;
    20     TGlobalVar.TApplicationObj.canvasObj = document.getElementById(TGlobalVar.TApplicationObj.id);
    21     TGlobalVar.TApplicationObj.canvasObj.width = w;
    22     TGlobalVar.TApplicationObj.canvasObj.height = h;
    23 };
    24 TMainWindow.prototype.onShow = function(){
    25     var self = this;
    26     if(TGlobalVar.TApplicationObj.canvas == null)return;
    27     TGlobalVar.TApplicationObj.canvas.clearRect(0,0,TGlobalVar.TApplicationObj.width,TGlobalVar.TApplicationObj.height);  
    28     self.show(self.childList);
    29 };
    30 TMainWindow.prototype.show = function(showlist,cood){
    31     if(cood == null)cood={x:0,y:0};
    32     var key = null;
    33     for(key in showlist){
    34         if(showlist[key].show){
    35             showlist[key].show(cood);
    36         }
    37     }
    38 };

    这个类实例化的时候有一个参数,是一个json对象,格式如下:

    1 {
    2         id:canvas的id,
    3         canvas的宽,
    4         height:canvas的高,
    5         frameRate:刷新频率
    6 }

    然后调用TMainWindow的init时,会自动保存这些数据,在其中用到了TGlobalVar.isAddMainWindow,TGlobalVar.TMainWindowObj,TGlobalVar.TApplicationObj这几个变量,其中TGlobalVar.isAddMainWindow是用来判断是否已经init过TMainWindow,因为界面上不可能有两个TMainWindow,所以用这个加以限制,而TGlobalVar.TMainWindowObj是用来保存界面上TMainWindow的变量,TGlobalVar.TApplicationObj是用来保存界面上TApplication对象的变量。

    在TMainWindow类中,还有个onShow和show方法,这两个方法是用来刷新界面用的。onShow是负责清空界面和调用show方法用的。在show方法中通过遍历TMainWindow里的childList,调用遍历到的元素的show方法,实现重绘,也就是说,在childList里的元素必须有show方法才行。onShow方法在TApplication中会用到。

    具体TApplication里的代码如下:

     1 function TApplication(){
     2     var self = this;
     3     self.type = "TApplication";
     4     self.canvas = null;
     5     self.canvasObj = null;
     6     self.frameRate = 50;
     7     self.objectIndex = 0;
     8     self.id = null;
     9     self.driver = null;
    10     
    11     TGlobalVar.TApplicationObj = self;
    12 }
    13 TApplication.prototype.exec = function(){
    14     var self = this;
    15     
    16     if(TGlobalVar.isAddApplication == true)return;
    17     TGlobalVar.isAddApplication = true;
    18     self.canvas = self.canvasObj.getContext("2d");
    19     self.driver = setInterval(function(){TGlobalVar.TMainWindowObj.onShow();},self.frameRate);
    20 };

    这个类应该是在TMainWindow前实例化,否则就会报错。但是在调用成员函数exec时,要在TMainWindow对象调用init之后调用。这个exec相当于是一个开启游戏重画的函数,也就是说,不调用这个函数界面将不能重画。在exec中又用到了TGlobalVar.isAddApplication这个变量,它和上文的TGlobalVar.isAddMainWindow的用途差不多,只不过一个是用来判断是否加入了TMainWindow对象,另一个是用来判断是否加入了TApplication对象。也就是说,我们使用的时候只能有一个TMainWindow对象和一个TApplication对象。用TGlobalVar.TApplicationObj和TGlobalVar.TMainWindowObj分别保存TApplication和TMainWindow对象是为了方便以后的操作。看到下面你就会明白。

    因为在TGlobalVar中加了几个成员属性,因此修改TGlobalVar,完整TGlobalVar代码如下:

    1 var TGlobalVar = function(){this.type = "TGlobalVar";};
    2 TGlobalVar.TApplicationObj = null;
    3 TGlobalVar.TMainWindowObj = null;
    4 TGlobalVar.isAddApplication = false;
    5 TGlobalVar.isAddMainWindow = false;

    有了这些,我们就可以用来显示图片了~~

    二,用于显示图片的TImage类

    上面我们已经把一些公有属性都保存好了,因此我们要实现绘画就很简单了。先看TImage类的代码:

     1 function TImage(){
     2     var self = this;
     3     
     4     self.type = "TImage";
     5     self.x = 0;
     6     self.y = 0;
     7     self.scaleX = 1;
     8     self.scaleY = 1;
     9     self.isLoadComplete = false;
    10     self.content = null;
    11     self.sx = 0;
    12     self.sy = 0;
    13     self.swidth = 0;
    14     self.sheight = 0;
    15     self.width = 0;
    16     self.height = 0;
    17     self.toSwidth = self.swidth;
    18     self.toSheight = self.sheight;
    19     self.toSx = self.sx;
    20     self.toSy = self.sy;
    21 }
    22 TImage.prototype.load = function(u){
    23     var self = this;
    24     self.isLoadComplete = false;
    25     self.content = new Image();
    26     self.content.onload = function(){
    27         self.content.onload = null;
    28         self.swidth = self.content.width;
    29         self.sheight = self.content.height;
    30         self.width = self.content.width;
    31         self.height = self.content.height;
    32         self.isLoadComplete = true;
    33     };
    34     self.content.src = u;
    35 };
    36 TImage.prototype.draw = function(layer){
    37     var self = this;
    38     if(!layer)layer = TGlobalVar.TMainWindowObj;
    39     layer.childList.push(self);
    40     self.parent = layer;
    41 };
    42 TImage.prototype.scale = function(scaleX,scaleY){
    43     var self = this;
    44     
    45     self.scaleX = scaleX || self.scaleX;
    46     self.scaleY = scaleY || self.scaleY;
    47 };
    48 TImage.prototype.move = function(x,y){
    49     var self = this;
    50     
    51     self.x = x || self.x;
    52     self.y = y || self.y;
    53 };
    54 TImage.prototype.setProperties = function(sx,sy,swidth,sheight){
    55     var self = this;
    56     
    57     self.toSwidth = swidth || self.swidth;
    58     self.toSheight = sheight || self.sheight;
    59     self.toSx = sx || self.sx;
    60     self.toSy = sy || self.sy;
    61 };
    62 TImage.prototype.show = function(){
    63     var self = this;
    64     
    65     if(self.isLoadComplete == false)return;
    66     self.swidth = self.toSwidth || self.swidth;
    67     self.sheight = self.toSheight || self.swidth;
    68     self.sx = self.toSx || 0;
    69     self.sy = self.toSy || 0;
    70     self.width = self.swidth;
    71     self.height = self.sheight;
    72     
    73     TGlobalVar.TApplicationObj.canvas.drawImage(
    74         self.content,
    75         self.sx,self.sy,
    76         self.swidth,self.sheight,
    77         self.x,self.y,
    78         self.width*self.scaleX,
    79         self.height*self.scaleY
    80     );
    81 };

    这里面的代码不难理解,显示图片的部分主要在show函数中。要加载图片需要在load函数中加载,也就是说,要显示图片需要先load一遍。另外,为了show函数并不需要自己去调用,在TGlobalVar.TMainWindowObj.show里面已经自动调用了。不过要显示图片要调用draw函数,这个函数有个参数,这个参数代表显示层,不添的话就是默认的最底层,这个参数现在只能不赋值,因为现在还没实现到层次化这一部分。

    要移动图片的话,调用move方法就可以了。参数:[x坐标, y坐标]

    要设置显示部分图片调用setProperties就行了,参数是:[图片可视范围x, 图片可视范围y, 图片可视范围宽度, 图片可视范围高度]

    要拉伸图片就调用scale函数,参数[x轴拉伸倍数, y轴拉伸倍数]

    好了,我们有了TImage类,就来做个测试吧,测试代码:

     1 <!DOCTYPE html>
     2 <html>
     3 <head>
     4 <meta charset="utf-8" />
     5 <title>显示一张图片</title>
     6 <script src="./TMain.js"></script>
     7 <script src="./TApplication.js"></script>
     8 <script src="./TMainWindow.js"></script>
     9 <script src="./TImage.js"></script>
    10 <script>
    11 function main(){
    12     var app = new TApplication();
    13     var mainwindow = new TMainWindow({
    14         id:"tomatogame",
    15         800,
    16         height:600,
    17         frameRate:50
    18     });
    19     mainwindow.init();
    20     app.exec();
    21     showImage();
    22 }
    23 function showImage(){
    24     var img = new TImage();
    25     img.load("./face.jpg");
    26     img.draw();
    27     img.move(20,20);
    28     img.scale(0.5,0.8);
    29     
    30     var img2 = new TImage();
    31     img2.load("./face.jpg");
    32     img2.draw();
    33     img2.move(150,150);
    34     img2.setProperties(100,100,200,200);
    35 }
    36 </script>
    37 </head>
    38 <body onload="main()">
    39     <canvas id="tomatogame"></canvas>
    40 </body>
    41 </html>

    运行出来的界面如下:

    over,是不是很简单?下一次我们来实现一下层次化效果。敬请期待~~

    源代码下载:点击这里下载源代码

    如果大家有不懂的地方,欢迎在文章下方留言。能看到你们的留言,是我最开心的事了~~如果文章有疏漏的地方,也欢迎指出,多谢大家的支持。

  • 相关阅读:
    栈和堆的区别【转】
    C++虚函数表解析(转)
    C++编码规范(转)
    全局变量的声明和定义 以及dll中全局变量的导出
    Sizeof与Strlen的区别与联系.
    利用事件对象实现线程同步
    创建互斥对象同步线程
    MFC GDI笔记 转
    ClientToScreen( )和ScreenToClient( )
    Visual C++线程同步技术剖析
  • 原文地址:https://www.cnblogs.com/knightls/p/3281387.html
Copyright © 2020-2023  润新知