游戏对象拒绝在画布上显示

时间:2013-12-18 12:42:32

标签: javascript canvas

以下是我所知道的事情:

(1)船舶对象在某种程度上被渲染,因为我在其渲染代码中添加了一个额外的圆圈并且正在绘制。

(2)使用我的旋转方法没有任何错误(至少对我来说没有什么不对),因为数字在控制台中正确显示。点top.xtop.ybottomLeft.xbottomLeft.ybottomRight.xbottomRight.y都是它们应该是的数字。

(3)使用(2)中提到的变量绘制船舶的实际代码工作正常。当我用硬编码的数字替换变量时,就会绘制出船。那是什么给出了什么?

错误代码位于第140行的Ship对象中,您可以在此处自行测试:http://noetherherenorthere.com/practice/landscape.html

  1 /* landscape.js */
  2 
  3 var canvas;
  4 var context;
  5 var landscape;
  6 var ship;
  7 
  8 function init(){
  9 
 10     canvas = document.getElementById('canvas');
 11     context = canvas.getContext('2d');
 12     landscape = new Landscape();
 13     ship = new Ship(375, 400);
 14     animate();
 15     
 16     window.addEventListener('keydown', function(e){
 17         switch(e.keyCode){
 18             case 37: // left
 19                 ship.theta -= 0.1;
 20                 ship.theta %= 2*Math.PI;
 21                 break;
 22             case 38: // up
 23                 ship.v_x += 1*Math.cos(ship.theta);
 24                 ship.v_y -= 1*Math.sin(ship.theta);
 25                 break;
 26             case 39: // right
 27                 ship.theta += 0.1;
 28                 ship.theta %= 2*Math.PI;
 29                 break;
 30             case 40: // down
 31                 // do nothing
 32                 break;
 33             default:
 34         }
 35     });
 36     
 37 }
 38 
 39 function animate(){
 40     
 41     if(this.i == null){
 42         this.i = 0;
 43     } else{
 44         this.i = (this.i + 0.1)%628; // switch it out after 100*2*PI cycles so i doesn't get too big.
 45     }
 46     
 47     landscape.sun.y += 10*Math.sin(this.i);
 48     landscape.moon.y -= 10*Math.cos(this.i);
 49     ship.v = Math.sqrt(Math.pow(ship.v_x, 2) + Math.pow(ship.v_y, 2)); 
 50     ship.x += (0.1 * ship.v_x);
 51     ship.y += (0.1 * ship.v_y);
 52     if(ship.x < 0){
 53         ship.x = canvas.width;
 54     }
 55     if(ship.x > canvas.width){
 56         ship.x = 0;
 57     }
 58     if(ship.y < 0){
 59         ship.y = canvas.height;
 60     }
 61     if(ship.y > canvas.height){
 62         ship.y = 0;
 63     }
 64     if(ship.v > ship.vMax){
 65         ship.v = ship.vMax;
 66     }
 67     if(ship.v < -ship.vMax){
 68         ship.v = -ship.vMax;
 69     }
 70     
 71     context.clearRect(0, 0, canvas.width, canvas.height);
 72     
 73     landscape.render();
 74     
 75     ship.render();
 76     
 77     // draw the dialog box  
 78     context.font = "14px Verdana";
 79     context.fillStyle = 'DodgerBlue';
 80     context.fillText("Velocity: (x: " + this.ship.v_x.toFixed(2) + ", y: " + this.ship.v_y.toFixed(2) + 
 81         ", angle: " + radToDeg(this.ship.theta).toFixed(2) + ")", 420, 465);
 82     context.fillText("Position: (x: " + this.ship.x.toFixed(2) + ", y: " + this.ship.y.toFixed(2) + ")", 420, 485);
 83     
 84     window.setTimeout(animate, 40);
 85     
 86 }
 87 
 88 function radToDeg(radians){
 89 
 90     return radians*(180/Math.PI);
 91     
 92 }
 93 
 94 function degToRad(degrees){
 95     
 96     return degrees*(Math.PI/180);
 97     
 98 }
 99 
100 function Landscape(){
101 
102     // order of the elements matters here
103 
104     this.sky = new Sky();
105     this.sun = new Sun(600, 150, 50);
106     this.moon = new Moon(100, 100, 50);
107     
108     this.render = function(){
109     
110         for(var element in this){
111             if(this[element].hasOwnProperty("render")){
112                 this[element].render();
113             }
114         }
115     
116     }
117 }
118 
119 function Ship(x, y){
120     
121     this.x = x;
122     this.y = y;
123     this.r = 10;
124     this.v = 0; // initial velocity of zero
125     this.v_x = 0;
126     this.v_y = 0;
127     this.theta = Math.PI/2; // starts out pointing upwards
128     this.vMax = 200;
129     this.render = function(){
130         var top = new Vector(this.x, this.y);
131         var bottomLeft = new Vector(this.x - 10, this.y + 30);
132         var bottomRight = new Vector(this.x + 10, this.y + 30);
133         
134         top.rotate(this.theta, true);
135         bottomLeft.rotate(this.theta, true);
136         bottomRight.rotate(this.theta, true);
137         
138         // console.log("top: " + top + ", bottomLeft: " + bottomLeft + ", bottomRight: " + bottomRight);
139         
140         context.fillStyle = 'SlateGray';
141         context.beginPath();
142         context.moveTo(top.x, top.y);
143         context.lineTo(bottomLeft.x, bottomLeft.y);
144         context.lineTo(bottomRight.x, bottomRight.y);
145         context.closePath();
146         context.fill();
147         
148         // this code works even though the code above doesn't.
149         // context.fillStyle = 'SlateGray';
150 //          context.beginPath();
151 //          context.moveTo(this.x, this.y);
152 //          context.lineTo(this.x - 10, this.y + 30);
153 //          context.lineTo(this.x + 10, this.y + 30);
154 //          context.closePath();
155 //          context.fill();
156         
157         context.fillStyle = 'LightGreen';
158         context.beginPath();
159         context.arc(200, 200, 30, 0, 2*Math.PI);
160         context.closePath();
161         context.fill();
162     }
163     
164     function Vector(x, y){
165         
166         this.x = x;
167         this.y = y;
168         this.rotate = function(theta, round){
169             
170             var rotationMatrix = new Matrix(2, 2, Math.cos(theta), -Math.sin(theta), Math.sin(theta), Math.cos(theta));
171             var vector = new Matrix(2, 1, this.x, this.y);
172             var resultVector = Matrix.multiply(rotationMatrix, vector);
173             this.x = resultVector[0][0];
174             this.y = resultVector[1][0];
175             if(round){
176                 this.x = Math.floor(this.x);
177                 this.y = Math.floor(this.y);
178             }
179             
180             return this;
181             
182         }
183         
184         this.toString = function(){
185             
186             return "x: " + x + ", y: " + y;
187             
188         }
189         
190     }
191     
192     function Matrix(rows, cols /*, var args */){
193     
194         // constructor
195         
196         if(rows == null || cols == null){
197             throw new Error("null rows or cols argument");
198         } else if(!isPositiveInteger(rows) || !isPositiveInteger(cols)){
199             throw new Error("rows and cols must be whole numbers");
200         } else if(rows > 1000 || cols > 1000){
201             throw new Error("rows and cols must be < 1000 in size");
202         }
203         
204         this.numRows = rows;
205         this.numCols = cols;
206         if(arguments.length - 2 > rows*cols){
207             throw new Error("too many arguments to Matrix constructor");
208         } else if(arguments.length > 2 && arguments.length - 2 < rows*cols){
209             throw new Error("too few arguments to Matrix constructor for initializing Matrix." + 
210                 " Usage: rows, cols [, row-major list of row and col entries]");
211         }
212         
213         if(rows === undefined){
214             console.log(arguments);
215         }
216         
217         for(var a = 0; a < rows; a++){
218             this[a] = new Array();
219         }
220         
221         for(var a = 2; a < arguments.length; a++){
222             try{
223                 var row = Math.floor((a - 2)/rows);
224                 var col = Math.floor((a - 2)%rows);
225                 this[row][col] = arguments[a];
226             } catch(e){
227                 console.log(row + ", " + col);
228                 throw e;
229             }
230         }   
231         
232     }
233     
234     Matrix.multiply = function(matrixA, matrixB){
235         
236         if(matrixA.numCols != matrixB.numRows){
237             throw new Error("# of cols in first matrix must equal # of rows in second matrix");
238         }
239     
240         var resultMatrix = new Matrix(matrixA.numRows, matrixB.numCols);
241         var sum, i, j, k;
242         
243         for(i = 0; i < matrixA.numRows; i++){
244             for(j = 0; j < matrixB.numCols; j++){
245                 sum = 0;
246                 for(k = 0; k < matrixA.numCols; k++){
247                     sum += matrixA[i][k] * matrixB[k][j];
248                 }
249                 resultMatrix[i][j] = sum;
250             }
251         }
252         
253         return resultMatrix;
254     }
255     
256     function isPositiveInteger(n){
257     
258         if(n == null){ return false; }
259         if(typeof n != "number"){ return false; }
260         if(!isFinite(n)){ return false; }
261         if(n <= 0){ return false; }
262         if(n%1 !== 0){ return false; }
263         return true;
264     
265     }
266                         
267 }
268 
269 function Sky(){
270 
271     this.x = 0;
272     this.y = 0;
273     this.width = canvas.width;
274     this.height = canvas.height;
275     this.render = function(){
276         context.fillStyle = 'Black'; // previously Indigo
277         context.fillRect(this.x, this.y, this.width, this.height);
278     }
279     
280 }
281 
282 function Sun(x, y, radius){
283 
284     this.x = x;
285     this.y = y;
286     this.r = radius;
287     this.render = function(){
288         context.fillStyle = 'Gold';
289         context.beginPath();
290         context.arc(this.x, this.y, this.r, 0, 2*Math.PI);
291         context.closePath();
292         context.fill();
293     }
294     
295 }
296 
297 function Moon(x, y, radius){
298 
299     this.x = x;
300     this.y = y;
301     this.r = radius;
302     this.render = function(){
303         context.fillStyle = 'LightYellow';
304         context.beginPath();
305         context.arc(this.x, this.y, this.r, 0, 2*Math.PI);
306         context.closePath();
307         context.fill();
308     }
309     
310 }

1 个答案:

答案 0 :(得分:0)

EDIT 12/19/13 5:02 PM:在发货离开屏幕和参考列表的情况下更新了正确的回合。

主要的问题是,当我绕着它的中心旋转船时,我正在原点旋转。所以我基本上把它扔掉了屏幕,这就是它没有出现的原因。我在废弃我的Matrix类并选择了更简单的canvas.rotate和canvas.translate方法来完成这项工作后发现了这个:

var top = new Point(this.x, this.y);
var bottomLeft = new Point(this.x - 10, this.y + 30);
var bottomRight = new Point(this.x + 10, this.y + 30);
var center = new Point((top.x + bottomLeft.x + bottomRight.x)/3, (top.y + bottomLeft.y + bottomRight.y)/3);

context.save();
context.translate(center.x, center.y);
context.rotate(-this.theta);
context.translate(-center.x, -center.y);

context.fillStyle = 'SlateGray';
context.beginPath();
context.moveTo(top.x, top.y);
context.lineTo(bottomLeft.x, bottomLeft.y);
context.lineTo(bottomRight.x, bottomRight.y);
context.closePath();
context.fill();

context.restore();

另外,我没有在keydown事件内旋转,而是为ship.rotatingLeft和ship.rotatingRight制作了布尔值。在keydown上,ship.rotatingLeft变为真,在keyup上变为假。当且仅当变量为真时,animate()方法才会旋转。因为动画似乎比keydown事件发射更频繁(每40毫秒)发生,所以船舶能够快速转弯,但也能顺利转动。 (之前,当它转动时,它要么变成乌龟,要么从一个位置跳到另一个位置。)

这是完整的代码。它与问题中提到的link相同。 (我试图制作一个JSFiddle,但无法让它正常工作。)

  1 /* landscape.js */
  2 
  3 var canvas;
  4 var context;
  5 var landscape;
  6 var ship;
  7 
  8 function init(){
  9 
 10     canvas = document.getElementById('canvas');
 11     context = canvas.getContext('2d');
 12     landscape = new Landscape();
 13     ship = new Ship(375, 400);
 14     animate();
 15     
 16     window.addEventListener('keydown', function(e){
 17         switch(e.keyCode){
 18             case 37: // left
 19                 ship.rotatingLeft = true;
 20                 break;
 21             case 38: // up
 22                 ship.thrusting = true;
 23                 break;
 24             case 39: // right
 25                 ship.rotatingRight = true;
 26                 break;
 27             case 40: // down
 28                 // do nothing
 29                 break;
 30             default:
 31         }
 32     });
 33     
 34     window.addEventListener('keyup', function(e){
 35         switch(e.keyCode){
 36             case 37: // left
 37                 ship.rotatingLeft = false;
 38                 break;
 39             case 38: // up
 40                 ship.thrusting = false;
 41             case 39: // right
 42                 ship.rotatingRight = false;
 43                 break;
 44             default:
 45         }
 46     });
 47     
 48 }
 49 
 50 function animate(){
 51     
 52     if(this.i == null){
 53         this.i = 0;
 54     } else{
 55         this.i = (this.i + 0.1)%628; // switch it out after 100*2*PI cycles so i doesn't get too big.
 56     }
 57     
 58     // move the sun and the moon in a periodic fashion
 59     
 60     landscape.sun.y += 10*Math.sin(this.i);
 61     landscape.moon.y -= 10*Math.cos(this.i);
 62     
 63     
 64     // if ship gets off screen, put it back at the same spot on the other side
 65     // not a proper wraparound
 66     
 67     if(ship.x < (0 - ship.maxBreadth) || ship.x > (canvas.width + ship.maxBreadth)){
 68         ship.x = mod(ship.x, canvas.width);
 69     }
 70     
 71     if(ship.y < (0 - ship.maxBreadth) || ship.y > (canvas.height + ship.maxBreadth)){
 72         ship.y = mod(ship.y, canvas.height);
 73     }
 74     
 75     // thrusting
 76     
 77     ship.v = Math.sqrt(Math.pow(ship.v_x, 2) + Math.pow(ship.v_y, 2)); 
 78     
 79     if(ship.thrusting && ship.v < ship.vMax){
 80         ship.v_x += 1*Math.cos(ship.theta + Math.PI/2);
 81         ship.v_y += 1*Math.sin(ship.theta + Math.PI/2);
 82     }
 83     
 84     ship.x += (0.1 * ship.v_x);
 85     ship.y -= (0.1 * ship.v_y);
 86     
 87     // rotation
 88     
 89     if(ship.rotatingLeft){
 90         ship.theta += 0.04;
 91         ship.theta %= 2*Math.PI;
 92     }
 93     
 94     if(ship.rotatingRight){
 95         ship.theta -= 0.04;
 96         ship.theta %= 2*Math.PI;
 97     }
 98     
 99     context.clearRect(0, 0, canvas.width, canvas.height);
100     
101     landscape.render();
102     
103     ship.render();
104     
105     // draw the velocity and position indicators
106         
107     context.font = "14px Verdana";
108     context.fillStyle = 'DodgerBlue';
109     context.fillText("Velocity: (x: " + this.ship.v_x.toFixed(2) + ", y: " + this.ship.v_y.toFixed(2) + 
110         ", angle: " + radToDeg(this.ship.theta).toFixed(2) + ")", 420, 465);
111     context.fillText("Position: (x: " + this.ship.x.toFixed(2) + ", y: " + this.ship.y.toFixed(2) + ")", 420, 485);
112     
113     window.setTimeout(animate, 40);
114     
115 }
116 
117 function radToDeg(radians){
118 
119     return radians*(180/Math.PI);
120     
121 }
122 
123 function degToRad(degrees){
124     
125     return degrees*(Math.PI/180);
126     
127 }
128 
129 function Landscape(){
130 
131     // order of the elements matters here
132 
133     this.sky = new Sky();
134     this.sun = new Sun(600, 150, 50);
135     this.moon = new Moon(100, 100, 50);
136     
137     this.render = function(){
138     
139         for(var element in this){
140             if(this[element].hasOwnProperty("render")){
141                 this[element].render();
142             }
143         }
144     
145     };
146 }
147 
148 function Ship(x, y){
149     
150     this.x = x;
151     this.y = y;
152     this.r = 10;
153     this.v = 0; // initial velocity of zero
154     this.v_x = 0;
155     this.v_y = 0;
156     this.theta = 0; // starts out pointing upwards
157     this.vMax = 200;
158     this.maxBreadth = 32; // approximate
159     this.rotatingLeft = false;
160     this.rotatingRight = false;
161     this.thrusting = false;
162     
163     this.render = function(){
164         this._render(this.x, this.y);
165         
166         // drawing a second time for wraparound
167         
168         if((ship.x < 0) || (ship.x > canvas.width) || (ship.y < 0) || (ship.y > canvas.height)){
169             this._render(mod(this.x, canvas.width), mod(this.y, canvas.height));
170         }
171     };
172     
173     this._render = function(x, y){ // internal render function
174         
175         var top = new Point(x, y);
176         var bottomLeft = new Point(x - 10, y + 30);
177         var bottomRight = new Point(x + 10, y + 30);
178         var center = new Point((top.x + bottomLeft.x + bottomRight.x)/3, (top.y + bottomLeft.y + bottomRight.y)/3);
179         
180         context.save();
181         context.translate(center.x, center.y);
182         context.rotate(-this.theta);
183         context.translate(-center.x, -center.y);
184         
185         context.fillStyle = 'SlateGray';
186         context.beginPath();
187         context.moveTo(top.x, top.y);
188         context.lineTo(bottomLeft.x, bottomLeft.y);
189         context.lineTo(bottomRight.x, bottomRight.y);
190         context.closePath();
191         context.fill();
192         
193         context.restore();
194         
195     };  
196                         
197 }
198 
199 function Point(x, y){
200     
201     this.x = x;
202     this.y = y;
203     this.rotate = function(theta, round){
204     
205         this.x = Math.cos(theta)*this.x - Math.sin(theta)*this.y;
206         this.y = Math.sin(theta)*this.x + Math.cos(theta)*this.y;
207         
208         if(round){
209             
210             this.x = Math.round(this.x);
211             this.y = Math.round(this.y);
212             
213         }
214         
215         return this; // for method chaining
216         
217     };
218     
219     this.toString = function(){
220         
221         return "x: " + this.x + ", y: " + this.y;
222         
223     };
224     
225 }
226 
227 function isPositiveInteger(n){
228 
229     if(n == null){ return false; }
230     if(typeof n != "number"){ return false; }
231     if(!isFinite(n)){ return false; }
232     if(n <= 0){ return false; }
233     if(n%1 !== 0){ return false; }
234     return true;
235 
236 }
237 
238 function Sky(){
239 
240     this.x = 0;
241     this.y = 0;
242     this.width = canvas.width;
243     this.height = canvas.height;
244     this.render = function(){
245         context.fillStyle = 'Black'; // previously Indigo
246         context.fillRect(this.x, this.y, this.width, this.height);
247     };
248     
249 }
250 
251 function Sun(x, y, radius){
252 
253     this.x = x;
254     this.y = y;
255     this.r = radius;
256     this.render = function(){
257         context.fillStyle = 'Gold';
258         context.beginPath();
259         context.arc(this.x, this.y, this.r, 0, 2*Math.PI);
260         context.closePath();
261         context.fill();
262     };
263     
264 }
265 
266 function Moon(x, y, radius){
267 
268     this.x = x;
269     this.y = y;
270     this.r = radius;
271     this.render = function(){
272         context.fillStyle = 'LightYellow';
273         context.beginPath();
274         context.arc(this.x, this.y, this.r, 0, 2*Math.PI);
275         context.closePath();
276         context.fill();
277     };
278     
279 }
280 
281 function mod(x, y) {
282         return ((x % y) + y) % y;
283 }

参考文献:

  1. 围绕固定点旋转:using HTML5 Canvas - rotate image about arbitrary point
  2. 模运算符:Javascript modulo not behaving
  3. 环绕:Wraparound for HTML5 Canvas: How to Get (Static) Shapes to Continue around Edges of Canvas
  4. 船舶中心公式:http://en.wikipedia.org/wiki/Centroid