以下是我所知道的事情:
(1)船舶对象在某种程度上被渲染,因为我在其渲染代码中添加了一个额外的圆圈并且正在绘制。
(2)使用我的旋转方法没有任何错误(至少对我来说没有什么不对),因为数字在控制台中正确显示。点top.x
,top.y
,bottomLeft.x
,bottomLeft.y
,bottomRight.x
和bottomRight.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 }
答案 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 }
参考文献: