让我尝试解释一下我现在拥有的东西,以及我想要增加的东西。
现在,我有一个画布,跟随精灵(播放器)进入500x899的背景图像(地图)上,该图片为5000x3750。两者都在同一个精灵表上。
现在,如果按下箭头键,则玩家精灵会沿着地图沿该方向移动,而画布会跟随玩家。
-但我想让左/右箭头键旋转播放器,而使上键向该方向移动。
这是我的代码,很多,但是井井有条:
// wrapper for "class" Rectangle
(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, width, 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;
// move speed in pixels per second
this.speed = 200;
// 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){
context.save();
// before draw we need to convert player world's position to canvas position
context.drawImage(imgSprite,1700,599, this.width, this.height,(this.x-this.width/2) - xView, (this.y-this.height/2) - yView, this.width, this.height);
context.restore();
}
// add "class" Player to our Game object
Game.Player = Player;
})();
// 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(){
this.x = 0;
this.y = 0;
var ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = this.width;
ctx.canvas.height = this.height;
ctx.drawImage(imgSprite,0,2250,this.width,this.height,this.x,this.y,this.width,this.height);
ctx.restore();
// store the generate map as this image texture
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;
}
// 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(this.image, 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)
};
// generate a large image texture for the room
room.map.generate();
// setup player
var player = new Game.Player(85, 80);
// setup the magic camera !!!
var camera = new Game.Camera(0, 0, 800, 599, room.width, room.height);
camera.follow(player, 800/2, 599/2);
// Game update function
var update = function(){
player.update(STEP, room.width, room.height);
camera.update();
}
// Game draw function
var draw = function(){
// clear the entire canvas
context.clearRect(0, 0, 800, 599);
// redraw all objects
room.map.draw(context, camera.xView, camera.yView);
player.draw(context, camera.xView, camera.yView);
}
// Game Loop
var gameLoop = function(){
update();
draw();
}
// <-- configure play/pause capabilities:
// I'll use setInterval instead of requestAnimationFrame for compatibility reason,
// but it's easy to change that.
var runningId = -1;
Game.play = function(){
if(runningId == -1){
runningId = setInterval(function(){
gameLoop();
}, INTERVAL);
console.log("play");
}
}
Game.togglePause = function(){
if(runningId == -1){
Game.play();
}
else
{
clearInterval(runningId);
runningId = -1;
console.log("paused");
}
}
})();
// <-- configure Game controls:
Game.controls = {
left: false,
up: false,
right: false,
down: false,
};
window.addEventListener("keydown", function(e){
switch(e.keyCode)
{
case 37: // left arrow
Game.controls.left = true;
break;
case 38: // up arrow
Game.controls.up = true;
break;
case 39: // right arrow
Game.controls.right = true;
break;
case 40: // down arrow
Game.controls.down = true;
break;
}
}, false);
window.addEventListener("keyup", function(e){
switch(e.keyCode)
{
case 37: // left arrow
Game.controls.left = false;
break;
case 38: // up arrow
Game.controls.up = false;
break;
case 39: // right arrow
Game.controls.right = false;
break;
case 40: // down arrow
Game.controls.down = false;
break;
case 80: // key P pauses the game
Game.togglePause();
break;
}
}, false);
// start the game when page is loaded
window.onload = function(){
Game.play();
}
同样,这对于画布可以跟随播放器及所有对象起作用,但是现在我尝试使用箭头键旋转播放器精灵。我用过谷歌并尝试了一些类似的代码:
var thrust = 0;
var rotation = 0;
ctxPlayer.translate(this.width/2, this.height/2);
ctxPlayer.rotate(this.angle);
ctxPlayer.translate(this.x,this.y);
if(Game.controls.left)
this.angle =-3
(对不起,我没有关于轮换的确切尝试,因为每次无效时我都会删除它们)。
使用与此类似的代码,我得到的唯一旋转就是玩家围绕坐标在一个巨大的圆圈内旋转。
(实际上并没有在一个位置旋转,也没有通过按键操作)
任何帮助或建议都非常感谢。
最佳答案
更改操作顺序。首先旋转精灵,然后移动它。您将希望精灵的中心在0,0(左上角)处,以获得想要的效果。以下代码假定您已经从0,0开始:
ctxPlayer.save();
ctxPlayer.translate(this.x,this.y);
ctxPlayer.rotate(this.angle);
ctxPlayer.translate(-this.width/2,-this.height/2); // before we draw the sprite lets set the anchor point to its center.
ctxPlater.restore();
http://jsfiddle.net/LRpBM/