为什么我的精灵图形算法有时会在绘图时留下空白?

时间:2017-08-09 22:52:28

标签: javascript css html5 canvas html5-canvas

我的朋友和我正在制作游戏。但是,我在明星精灵管理方面遇到了困难。有时,存在间隙(应该绘制精灵的区域,而不是)。我已将整个程序作为代码片段包含在内,但下面也是可能直接导致问题的代码块。

每个精灵都有坐标对。坐标图如下所示:

[{x:xCoordinate,y:yCoordinate},{x:xCoordinate,y:yCoordinate},...];

此代码生成并删除sprite ...

for(var i = 0; i < stars.positions.length; i++){
        //store coordinate positions...
        var x = stars.positions[i].x;
        var y = stars.positions[i].y;

        //delete sprites no longer visible within the viewport...
        if(x > window.innerWidth || x < -spriteWidth || y < -spriteHeight || y > window.innerHeight){
            //sprite is no longer visible within viewport; remove it...
            stars.positions.splice(i,1);
        }

        //find necessary comparative coordinates... 
        var lowestXCoordinatePair = stars.meta.lowestXCoordinatePair;
        var highestXCoordinatePair = stars.meta.highestXCoordinatePair;
        var lowestYCoordinatePair = stars.meta.lowestYCoordinatePair;
        var highestYCoordinatePair = stars.meta.highestYCoordinatePair;

        //gather star sprite meta data...
        var spriteWidth = stars.meta.spriteWidth;
        var spriteHeight = stars.meta.spriteHeight;

        if(lowestXCoordinatePair.x > 0){
            //Gap on the left side. New sprites necessary to fill the gap on left row...

            //console.log('adding sprites left row...')
            for(var i = 0; i < stars.meta.imagesYRequired; i++){
                stars.positions.push({
                    x:lowestXCoordinatePair.x-spriteWidth,
                    y:lowestXCoordinatePair.y+i*spriteHeight
                });
            }
        }

        if(highestXCoordinatePair.x < window.innerWidth-spriteWidth){
            //Gap on the right side. New sprites necessary to fill the gap on the right row...

            //console.log('adding sprites right row...')
            for(var i = 0; i < stars.meta.imagesYRequired; i++){
                stars.positions.push({
                    x:highestXCoordinatePair.x+spriteWidth,
                    y:highestXCoordinatePair.y+i*spriteHeight
                });
            }
        }

        if(lowestYCoordinatePair.y > 0){
            //Gap on the top side. New sprites necessary to fill the gap on the top row...

            //console.log('adding sprites top row...')
            for(var i = 0; i < stars.meta.imagesXRequired; i++){
                stars.positions.push({
                    x:lowestYCoordinatePair.x+i*spriteWidth,
                    y:lowestYCoordinatePair.y-spriteHeight
                });
            }
        }

        if(highestYCoordinatePair.y < window.innerHeight-spriteHeight){
            //Gap on the bottom side. New sprites necessary to fill the gap on the bottom row...

            console.log('adding sprites bottom row...')
            for(var i = 0; i < stars.meta.imagesXRequired; i++){
                stars.positions.push({
                    x:highestYCoordinatePair.x+i*spriteWidth,
                    y:highestYCoordinatePair.y+spriteHeight
                });
            }
        }

'use strict';


//global variables
var canvas, c;

//viewport variables
var viewportPosition = {
	x:false,
	y:false
};

//game matrix settings
var gameMatrixConfig = {
	width:20000,
	height:20000
};

//cursor position
var cursorPosition = {
	x:false,
	y:false
};

//spaceship position
var spaceship = {
    x:false,
    y:false,
    rotation:0,
    gameMatrixPositionX:false,
    gameMatrixPositionY:false
};

//fps monitor (for monitoring frame rate for development purposes)...
var fps = {
    lastFrameTime:undefined,
    timeSinceLastFrame:undefined,
    startDisplayTimeInterval:function(){
        setInterval(function(){
            document.getElementById('fpsLabel').innerHTML = Math.floor(1000/getTimeSinceLastFrame());
        },500);
    }
};

function getTimeSinceLastFrame(){
    return fps.timeSinceLastFrame;
}


//resize throttle timer global variable holder
var resizeTimer = false;

//the drawing frame:
var renderFrame = false;

//global events
window.addEventListener('load',function(){
	initialize('load');
});

window.addEventListener('resize',function(){
	clearTimeout(resizeTimer);
	resizeTimer = setTimeout(function(){
		initialize('resize');
	},100);
});

window.addEventListener('mousemove',function(e){
	cursorPosition.x = e.clientX;
	cursorPosition.y = e.clientY;
});


//global functions
function initialize(type){
    if(type == 'load'){
        preLoadSprites();
    }
    initializeCanvas();
}

function initializeCanvas(){
	canvas = document.getElementById('canvas');
	c = canvas.getContext('2d');
	canvas.width = window.innerWidth;
	canvas.height = window.innerHeight;
	c.width = canvas.width;
	c.height = canvas.height;
}

/*a class with a filePath argument.
This class is used to create new sprite images
from an instance of the class.*/
function sprite(filePath){
    //create a new image preload
    this.sprite = new Image();
    this.sprite.src = filePath;
    //load status
    this.loaded = false;
    /*
    holds all current positions of the sprite.
    default start coordinates are 0,0
    */
    
    this.positions = [];
    
    //original associative array data structure...
    //this.positions = {};
    
    
    //the image has been preloaded
    this.sprite.addEventListener('load',function(){
        this.loaded = true;
        /*
        bind the "this" reference of the constructor to avoid referencing
        the load event function
        */
    }.bind(this));
}

function preLoadSprites(){
    //create new objects for all the sprites to be used in the game
    var sprites = {
        stars:new sprite('https://drive.google.com/uc?export=download&id=0B821h2dKD0r_bERZb0RHeVRTQnM')
    };
    
    /*
    check the load status of the sprites
    loop through all properties of sprites object twice per second
    check for load status. Load flag default is true. Set flag to false
    when an unloaded image is discovered.
    */
    var interval = setInterval(function(){
        var x, loaded = true;
        for(x in sprites){
            if(!sprites[x].loaded){
                loaded = false;
            }
        }
        if(loaded){
            clearInterval(interval);
            //complete other tasks such as hiding a load spinner...
            
            //ready sprites and context for graphing and provide access to sprites....
            initializeGraphing(sprites);
        }
    },50);
}

function initializeGraphing(sprites){
    //set initial viewport position in game matrix....
    viewportPosition.x = gameMatrixConfig.width/2;
    viewportPosition.y = gameMatrixConfig.height/2;
    
    //start graph animation loop; provide access to sprites....
    graph(sprites);
}

/*research how to inherit values from another object with
local variables.*/
function graph(sprites){
    updateSpritePositions(sprites);
    //testing frame rate...
    setInterval(function(){draw()},16.67);
    //60fps interval code. Uncomment when testing is complete...
    //setInterval(function(){draw()},16.67);
    
    //calculate sprite requirements for viewport and configure a sprite matrix...
    initializeStars(sprites.stars);
    
    fps.startDisplayTimeInterval();
    
    //render the graphic frame
    function draw(){
        //console.log(fps.timeSinceLastFrame)
        fps.timeSinceLastFrame = Date.now()-fps.lastFrameTime;
        //fps.displayTimeInterval();
        //fps.lastFrameTime = Date.now();
        
        //clear the canvas
        c.clearRect(0,0,window.innerWidth,window.innerHeight);
        //graph the stars...
        graphStars(sprites.stars);
        updateSpritePositions(sprites);
        fps.lastFrameTime = Date.now();
    }
}

function initializeStars(stars){
    /*
    calculate sprite requirements for viewport and configure a sprite matrix
    this only needs to happen once unless the viewport is resized
    */
    
    /*
    meta data used for various calculations throughout the script...
    */
    stars.meta = {
        //required sprites to fill the viewport width (based on sprite width)...
        imagesXRequired:Math.ceil(window.innerWidth/(stars.sprite.width))+2,
        //required sprites to fill the viewport height (based on sprite height)...
        imagesYRequired:Math.ceil(window.innerHeight/(stars.sprite.height))+2,
        //required sprites to fill the entire viewport...
        get requiredSprites(){
            return this.imagesXRequired*this.imagesYRequired;
        },
        //the sprite width...
        spriteWidth:stars.sprite.width,
        //the sprite height...
        spriteHeight:stars.sprite.height,
        //the lowest x value in stars.positions...
        get lowestXCoordinatePair(){
            var xCoordinates = [];
            var yCoordinates = [];
            for(var i = 0; i < stars.positions.length; i++){
                xCoordinates.push(stars.positions[i].x);
                yCoordinates.push(stars.positions[i].y);
            }
            var x = Math.min.apply(Math, xCoordinates);
            var index = xCoordinates.indexOf(x);
            var y = yCoordinates[index];

            return {
                x:x,
                y:y
            };
        },
        //the highest x value in stars.positions...
        get highestXCoordinatePair(){
            var xCoordinates = [];
            var yCoordinates = [];
            for(var i = 0; i < stars.positions.length; i++){
                xCoordinates.push(stars.positions[i].x);
                yCoordinates.push(stars.positions[i].y);
            }
            var x = Math.max.apply(Math, xCoordinates);
            var index = xCoordinates.indexOf(x);
            var y = yCoordinates[index];

            return {
                x:x,
                y:y
            };
        },
        //the lowest y value in stars.positions...
        get lowestYCoordinatePair(){
            var xCoordinates = [];
            var yCoordinates = [];
            for(var i = 0; i < stars.positions.length; i++){
                xCoordinates.push(stars.positions[i].x);
                yCoordinates.push(stars.positions[i].y);
            }
            var y = Math.min.apply(Math, yCoordinates);
            var index = yCoordinates.indexOf(y);
            var x = xCoordinates[index];

            return {
                x:x,
                y:y
            };
        },
        //the highest y value in stars.positions...
        get highestYCoordinatePair(){
            var xCoordinates = [];
            var yCoordinates = [];
            for(var i = 0; i < stars.positions.length; i++){
                xCoordinates.push(stars.positions[i].x);
                yCoordinates.push(stars.positions[i].y);
            }
            var y = Math.max.apply(Math, yCoordinates);
            var index = yCoordinates.indexOf(y);
            var x = xCoordinates[index];

            return {
                x:x,
                y:y
            };
        }
    };
    
    //the y coordinate in a scaled matrix system for sprites...
    var y = 0;
    //the x coordinate in a scaled matrix system for sprites...
    var x = 0;
    //loop through the number of required sprites and graph...
    for(var i = 0; i < stars.meta.requiredSprites; i++){      
        //calculate when a new row is necessary
        if((i)%stars.meta.imagesXRequired == 0){
            x = 0;
            y++;
        }
        
        //set actual starting viewport matrix coordinate positions....
        stars.positions[i] = {
            x:x*stars.meta.spriteWidth,
            y:y*stars.meta.spriteWidth
        };
        x++;
    }
}

function graphStars(stars){
    /*
    prior to graphing, determine if a sprite is no longer within
    the viewport matrix and remove it...
    
    if new sprites are necessary, add new coordinates accordingly...
    */
    
    
    
    /*
    ==============IMPORTANT NOTE==================
    
    */
    
    for(var i = 0; i < stars.positions.length; i++){
        //store coordinate positions...
        var x = stars.positions[i].x;
        var y = stars.positions[i].y;

        //delete sprites no longer visible within the viewport...
        if(x > window.innerWidth || x < -spriteWidth || y < -spriteHeight || y > window.innerHeight){
            //sprite is no longer visible within viewport; remove it...
            stars.positions.splice(i,1);
        }

        //find necessary comparative coordinates... 
        var lowestXCoordinatePair = stars.meta.lowestXCoordinatePair;
        var highestXCoordinatePair = stars.meta.highestXCoordinatePair;
        var lowestYCoordinatePair = stars.meta.lowestYCoordinatePair;
        var highestYCoordinatePair = stars.meta.highestYCoordinatePair;

        //gather star sprite meta data...
        var spriteWidth = stars.meta.spriteWidth;
        var spriteHeight = stars.meta.spriteHeight;

        if(lowestXCoordinatePair.x > 0){
            //Gap on the left side. New sprites necessary to fill the gap on left row...

            //console.log('adding sprites left row...')
            for(var i = 0; i < stars.meta.imagesYRequired; i++){
                stars.positions.push({
                    x:lowestXCoordinatePair.x-spriteWidth,
                    y:lowestXCoordinatePair.y+i*spriteHeight
                });
            }
        }

        if(highestXCoordinatePair.x < window.innerWidth-spriteWidth){
            //Gap on the right side. New sprites necessary to fill the gap on the right row...

            //console.log('adding sprites right row...')
            for(var i = 0; i < stars.meta.imagesYRequired; i++){
                stars.positions.push({
                    x:highestXCoordinatePair.x+spriteWidth,
                    y:highestXCoordinatePair.y+i*spriteHeight
                });
            }
        }

        if(lowestYCoordinatePair.y > 0){
            //Gap on the top side. New sprites necessary to fill the gap on the top row...

            //console.log('adding sprites top row...')
            for(var i = 0; i < stars.meta.imagesXRequired; i++){
                stars.positions.push({
                    x:lowestYCoordinatePair.x+i*spriteWidth,
                    y:lowestYCoordinatePair.y-spriteHeight
                });
            }
        }

        if(highestYCoordinatePair.y < window.innerHeight-spriteHeight){
            //Gap on the bottom side. New sprites necessary to fill the gap on the bottom row...

            console.log('adding sprites bottom row...')
            for(var i = 0; i < stars.meta.imagesXRequired; i++){
                stars.positions.push({
                    x:highestYCoordinatePair.x+i*spriteWidth,
                    y:highestYCoordinatePair.y+spriteHeight
                });
            }
        }

        c.drawImage(stars.sprite,x,y);
    }
}

function updateViewportPosition(){
    
}

function updateSpritePositions(sprites){
    
    /*gather information from the cursor to influence the next frame data
    cursor is a local object variable template for use within this function only
    cursor stores information about the cursor's position
    */
    
    var cursor = {
        distance:{
            //the cursor's distances on the planes from the origin for X and Y
            cursorXDistance:Math.abs(window.innerWidth/2-cursorPosition.x),
            cursorYDistance:Math.abs(window.innerHeight/2-cursorPosition.y)
        },
        quadrant:function(){
            //method returns the appropriate quadrant number for the unit circle
            if(cursorPosition.x > window.innerWidth/2 && cursorPosition.y < window.innerHeight/2){
                //first quadrant
                return 1;
            }
            if(cursorPosition.x > window.innerWidth/2 && cursorPosition.y > window.innerHeight/2){
                //fourth quadrant
                return 4;
            }
            if(cursorPosition.x < window.innerWidth/2 && cursorPosition.y < window.innerHeight/2){
                //second quadrant
                return 2;
            }
            if(cursorPosition.x < window.innerWidth/2 && cursorPosition.y > window.innerHeight/2){
                //third quadrant
                return 3;
            }
        }
    };
    
    //calculate the velocity (the number of pixels to move for the next frame)...
    function velocity(){
        /*
        To calculate velocity ratio, divide the hypotenuse of the cursor's position
        by the viewport hypotenuse.
        */        
        return Math.sqrt(Math.pow(cursor.distance.cursorXDistance,2) + Math.pow(cursor.distance.cursorYDistance,2))/100;
    }
    
    //calculate the movement ratio: the number of x pixels per y pixels
    function movementRatio(){
        var xRatio = cursor.distance.cursorXDistance/(cursor.distance.cursorYDistance+cursor.distance.cursorXDistance);
        var yRatio = cursor.distance.cursorYDistance/(cursor.distance.cursorYDistance+cursor.distance.cursorXDistance);
        return {xRatio,yRatio};
    }
    
    //update positions of sprites...
    
    //retrieve the current movement ratio object...
    var coordinateChange = movementRatio();
    //retrieve the current quadrant of the unit circle for the cursor's position...
    var quadrant = cursor.quadrant();
    //retrieve velocity coefficient...
    var velocity = velocity();
    //update viewport position based on quadrant position...
    
    var i;
    for(i in sprites.stars.positions){
        if(quadrant == 1){
            //update star sprite position
            sprites.stars.positions[i].x -= coordinateChange.xRatio*velocity;
            sprites.stars.positions[i].y += coordinateChange.yRatio*velocity;
        }
        if(quadrant == 2){
            //update star sprite position
            sprites.stars.positions[i].x += coordinateChange.xRatio*velocity;
            sprites.stars.positions[i].y += coordinateChange.yRatio*velocity;
        }
        if(quadrant == 3){
            //update the star sprite position
            sprites.stars.positions[i].x += coordinateChange.xRatio*velocity;
            sprites.stars.positions[i].y -= coordinateChange.yRatio*velocity;
        }
        if(quadrant == 4){
            //update star sprite position
            sprites.stars.positions[i].x -= coordinateChange.xRatio*velocity;
            sprites.stars.positions[i].y -= coordinateChange.yRatio*velocity;
        }
    }
}
html,body{
	width:100%;
	height:100%;
	margin:0;
	padding:0;
}

canvas{
	width:100%;
	height:100%;
	margin-bottom:-8px;
	background:#434343;
}

#gameInterface{
	width:100%;
	height:100%;
}

.hidden{
	width:1px;
	height:1px;
	position:fixed;
	top:0;
	left:0;
	z-index:-100;
}
<!DOCTYPE HTML>
<html>
<head>
	<meta charset="UTF-8">
	<title>Canvas Game v0.35</title>
	<!--<link rel="stylesheet" href="css/index.css" type="text/css">-->
	<!--<script type="text/javascript" src="js/index.js"></script>-->
</head>
<body>
    <div id="fpsLabel" style="position:fixed; top:0; left:0; display:table; background:#fff;">
    
    </div>
	<div id="intro">
	
	</div>
	<div id="gameInterface">
		<canvas id="canvas"></canvas>
	</div>
</body>
</html>

我已经集思广益,列出了问题的可能原因:

  • 过早删除精灵会造成差距。
  • 由于没有产生精灵而造成差距。
  • (子项)产生条件是正确的,但创建新行的循环是有缺陷的。
  • (子项目)产卵条件存在缺陷。

有人可以帮助我理解为什么偶尔存在差距并帮助我了解如何解决这个问题?谢谢。

0 个答案:

没有答案