Html5画布游戏,创建比观看画布大得多的地图

时间:2013-07-22 08:20:20

标签: javascript html5 canvas html5-canvas viewport

所以让我首先说我正在尝试创建一个大图像或房间,比如5000乘3750,并且画布或观看区域只有800乘599,总是跟随播放器块。我确实找到了一个如何做到这一点的好指南,使用了java脚本中绘制的背景和播放器(不是从精灵表中获取)。但我所拥有的是带有背景的精灵表,而玩家,我让玩家在JavaScript的背景上工作,但不是像我想要的那样从精灵表中获取。 以下是一些代码:

// wrapper for "class" Map
(function(){
    function Map(width, height){
// map dimensions
    this.width = width;
    this.height = height;       
// map texture
    this.image = null;
    }
// generate an example of a large map
    Map.prototype.generate = function(){
    ctxBg.drawImage(imgSprite,0,2250,5000,3750,0,0,5000,3750);  
    }       
// draw the map adjusted to camera
    Map.prototype.draw = function(context, xView, yView){                       
    var sx, sy, dx, dy;
    var sWidth, sHeight, dWidth, dHeight;   
// offset point to crop the image
    sx = xView;
    sy = yView; 
// dimensions of cropped image          
    sWidth =  800;
    sHeight = 599;
// if cropped image is smaller than canvas we need to change the source dimensions
    if(800 - sx < sWidth){
        sWidth = 800 - sx;
    }
    if(599 - sy < sHeight){
        sHeight = 599 - sy; 
    }           
// location on canvas to draw the croped image
    dx = 0;
    dy = 0;
// match destination with source to not scale the image
    dWidth = sWidth;
    dHeight = sHeight;                                  
    context.drawImage(imgSprite, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);         
    }       
// add "class" Map to our Game object
    Game.Map = Map; 
    })();
// Game Script
(function(){
// prepaire our game canvas
    var canvas = document.getElementById("gameCanvas");
    var context = canvas.getContext("2d");
// game settings:   
var FPS = 30;
var INTERVAL = 1000/FPS; // milliseconds
var STEP = INTERVAL/1000 // seconds     
// setup an object that represents the room
var room = {
width: 5000,
height: 3750,
map: new Game.Map(5000, 3750)
};  
room.map.generate(); 

继承了camra / player代码:

(function(){
    function Rectangle(left, top, width, height){
    this.left = left || 0;
    this.top = top || 0;
    this.right = (left + width) || 0;
    this.bottom = (top + height) || 0;
    }

Rectangle.prototype.set = function(left, top, /*optional*/width, /*optional*/height){
    this.left = left;
    this.top = top;
    this.width = width || this.width;
    this.height = height || this.height;
    this.right = (this.left + this.width);
    this.bottom = (this.top + this.height);
    }
Rectangle.prototype.within = function(r) {
    return (r.left <= this.left && 
        r.right >= this.right &&
        r.top <= this.top && 
        r.bottom >= this.bottom);
    }       
Rectangle.prototype.overlaps = function(r) {
    return (this.left < r.right && 
            r.left < this.right && 
            this.top < r.bottom &&
            r.top < this.bottom);
    }
// add "class" Rectangle to our Game object
Game.Rectangle = Rectangle;
    })();   
    // wrapper for "class" Camera (avoid global objects)
(function(){
    // possibles axis to move the camera
    var AXIS = {
        NONE: "none", 
        HORIZONTAL: "horizontal", 
        VERTICAL: "vertical", 
        BOTH: "both"
    };
    // Camera constructor
function Camera(xView, yView, canvasWidth, canvasHeight, worldWidth, worldHeight)
    {
// position of camera (left-top coordinate)
    this.xView = xView || 0;
    this.yView = yView || 0;        
// distance from followed object to border before camera starts move
    this.xDeadZone = 0; // min distance to horizontal borders
    this.yDeadZone = 0; // min distance to vertical borders         
// viewport dimensions
    this.wView = 800;
    this.hView = 599;                       
// allow camera to move in vertical and horizontal axis
    this.axis = AXIS.BOTH;          
// object that should be followed
    this.followed = null;           
// rectangle that represents the viewport
    this.viewportRect = new Game.Rectangle(this.xView, this.yView, this.wView, this.hView);                                     
// rectangle that represents the world's boundary (room's boundary)
    this.worldRect = new Game.Rectangle(this.xView, this.yView, this.wView, this.hView);        
    }
// gameObject needs to have "x" and "y" properties (as world(or room) position)
    Camera.prototype.follow = function(gameObject, xDeadZone, yDeadZone)
    {       
    this.followed = gameObject; 
    this.xDeadZone = xDeadZone;
    this.yDeadZone = yDeadZone;
    }                       
    Camera.prototype.update = function()
    {
// keep following the player (or other desired object)
    if(this.followed != null)
    {       
    if(this.axis == AXIS.HORIZONTAL || this.axis == AXIS.BOTH)
    {       
// moves camera on horizontal axis based on followed object position
    if(this.followed.x - this.xView  + this.xDeadZone > this.wView)
        this.xView = this.followed.x - (this.wView - this.xDeadZone);
    else if(this.followed.x  - this.xDeadZone < this.xView)
        this.xView = this.followed.x  - this.xDeadZone;     
    }
    if(this.axis == AXIS.VERTICAL || this.axis == AXIS.BOTH)
    {
// moves camera on vertical axis based on followed object position
    if(this.followed.y - this.yView + this.yDeadZone > this.hView)
        this.yView = this.followed.y - (this.hView - this.yDeadZone);
    else if(this.followed.y - this.yDeadZone < this.yView)
        this.yView = this.followed.y - this.yDeadZone;
    }                               
    }       
// update viewportRect
    this.viewportRect.set(this.xView, this.yView);  
// don't let camera leaves the world's boundary
    if(!this.viewportRect.within(this.worldRect))
{
    if(this.viewportRect.left < this.worldRect.left)
        this.xView = this.worldRect.left;
    if(this.viewportRect.top < this.worldRect.top)                  
        this.yView = this.worldRect.top;
    if(this.viewportRect.right > this.worldRect.right)
        this.xView = this.worldRect.right - this.wView;
    if(this.viewportRect.bottom > this.worldRect.bottom)                    
        this.yView = this.worldRect.bottom - this.hView;
    }
    }   
// add "class" Camera to our Game object
        Game.Camera = Camera;
    })();
// wrapper for "class" Player
(function(){
    function Player(x, y){
// (x, y) = center of object
// ATTENTION:
// it represents the player position on the world(room), not the canvas position
    this.x = x;
    this.y = y; 
    this.srcX = 1700;
    this.srcY = 599;
    this.drawX = 350;
    this.drawY = 400;   
    xView = this.x-this.width/2;
    yView = this.y-this.height/2;           
// move speed in pixels per second
    this.speed = 100;           
// render properties
    this.width = 85;
    this.height = 80;
    }
Player.prototype.update = function(step, worldWidth, worldHeight){
// parameter step is the time between frames ( in seconds ) 
// check controls and move the player accordingly
    if(Game.controls.left)
        this.x -= this.speed * step;
    if(Game.controls.up)
        this.y -= this.speed * step;
    if(Game.controls.right)
        this.x += this.speed * step;
    if(Game.controls.down)
        this.y += this.speed * step;        
// don't let player leaves the world's boundary
    if(this.x - this.width/2 < 0){
        this.x = this.width/2;
    }
    if(this.y - this.height/2 < 0){
        this.y = this.height/2;
    }
    if(this.x + this.width/2 > worldWidth){
        this.x = worldWidth - this.width/2;
    }
    if(this.y + this.height/2 > worldHeight){
        this.y = worldHeight - this.height/2;
    }
    }
Player.prototype.draw = function(/*context,*/ xView, yView){
    ctxPlayer.clearRect(0,0,800,599);
    context.save();
    ctxPlayer.drawImage(imgSprite,this.srcX,this.srcY,this.width,this.height,(this.x-this.width/2),(this.y-this.height/2),this.width,this.height);
    context.restore();          
    }
// add "class" Player to our Game object
    Game.Player = Player;   
    })();

它显示了图像和播放器,但是画布不跟随播放器对象,但是如果我使用这样的背景,它会这样做:

(function(){
function Map(width, height){
this.width = width;
this.height = height;
this.image = null;
}
Map.prototype.generate = function(){
var ctx = document.createElement("canvas").getContext("2d");        
ctx.canvas.width = this.width;
ctx.canvas.height = this.height;        
var rows = ~~(this.width/44) + 1;
var columns = ~~(this.height/44) + 1;
var color = "red";              
ctx.save();         
ctx.fillStyle = "red";          
for (var x = 0, i = 0; i < rows; x+=44, i++) {
ctx.beginPath();            
for (var y = 0, j=0; j < columns; y+=44, j++) {            
ctx.rect (x, y, 40, 40);                
}
color = (color == "red" ? "blue" : "red");
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();            
}       
ctx.restore();
this.image = new Image();
this.image.src = ctx.canvas.toDataURL("image/png");                 
// clear context
ctx = null;
}
// draw the map adjusted to camera
Map.prototype.draw = function(context, xView, yView){                   
var sx, sy, dx, dy;
var sWidth, sHeight, dWidth, dHeight;
// offset point to crop the image
sx = xView;
sy = yView;
// dimensions of cropped image          
sWidth =  context.canvas.width;
sHeight = context.canvas.height;
// if cropped image is smaller than canvas we need to change the source dimensions
if(this.image.width - sx < sWidth){
sWidth = this.image.width - sx;
}
if(this.image.height - sy < sHeight){
sHeight = this.image.height - sy; 
}
dx = 0;
dy = 0;
dWidth = sWidth;
dHeight = sHeight;                                  
context.drawImage(this.image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);            
}
Game.Map = Map;
})();

我环顾四周,做了一些谷歌搜索并使用了JavaScript检查器,但没有运气。任何建议/意见都非常感谢。

1 个答案:

答案 0 :(得分:2)

反正我会试一试。所以,对于理论部分:

您要实现的是简单的场景管理。场景需要一个Camera,一个存储X和Y偏移的对象以及它要显示的宽度和高度(宽度和高度也称为3D图形中的投影平面)。在每一帧中,您将通过相机的偏移来绘制场景(或世界)。

执行:

要在大型Canvas上绘制一个大图像,只需使用drawImage()函数和所有9个参数。

要绘制像Tiles这样的小图像,我建议看一下场景图,我前段时间在Collision detection in HTML5 canvas. Optimization too

写了一篇更深入的答案

如果每帧绘制多个对象,请注意您始终可以创建不在html DOM中的画布对象来缓存绘制结果,这对于获得良好的性能是必要的。绘制调用很昂贵,因为它的渲染状态更改和流式传输成本,而不是因为像素本身。

最后,要在顶部绘制你的角色,你需要某种z索引,所以你的绘制循环知道玩家在地面上,你可以通过图层或存储游戏对象的az索引来做到这一点。

到目前为止,你已经走上正轨了!