46 * |
46 * |
47 * Make the worm walk on the ground. |
47 * Make the worm walk on the ground. |
48 * @return Final position. |
48 * @return Final position. |
49 */ |
49 */ |
50 Vector PhysicsObject::walk (bool right) { |
50 Vector PhysicsObject::walk (bool right) { |
51 Vector cursor = right ? this->position + Vector(1,0) : this->position + Vector(-1,0); |
51 Vector cursor = right ? this->position + Vector(1,0) : this->position + Vector(-1,0); |
52 Vector reached = this->position; |
52 Vector reached = this->position; |
53 |
53 |
54 //for(int steps = 0; steps < 3; steps++) { |
54 //for(int steps = 0; steps < 3; steps++) { |
55 |
55 |
56 // Go up but not if the wall is over two pixels |
56 // Go up but not if the wall is over two pixels |
57 if(world.getType(cursor) != EMPTY) { |
57 if(world.getType(cursor) != EMPTY) { |
58 for(int height = 0, max = 3; height < max+42; height++) { |
58 for(int height = 0, max = 3; height < max+42; height++) { |
59 |
59 |
60 if(height >= max) |
60 if(height >= max) |
61 return reached; |
61 return reached; |
62 |
62 |
63 cursor.y--; |
63 cursor.y--; |
64 if(world.getType(cursor) == EMPTY) { |
64 if(world.getType(cursor) == EMPTY) { |
65 // Check that the other parts of the worm don't collide with anything |
65 // Check that the other parts of the worm don't collide with anything |
66 if(possibleLocation(cursor)) { |
66 if(possibleLocation(cursor)) { |
67 reached = cursor; |
67 reached = cursor; |
68 continue; |
68 continue; |
69 } else { |
69 } else { |
70 // Can't get any further |
70 // Can't get any further |
71 return reached; |
71 return reached; |
72 } |
72 } |
73 } |
73 } |
74 } |
74 } |
75 } else { |
75 } else { |
76 if(possibleLocation(cursor)) { |
76 if(possibleLocation(cursor)) { |
77 reached = cursor; |
77 reached = cursor; |
78 } |
78 } |
79 // Start checking if the lower squares are empty |
79 // Start checking if the lower squares are empty |
80 for(int depth = 0, max = 3; depth < max+42; depth++) { |
80 for(int depth = 0, max = 3; depth < max+42; depth++) { |
81 |
81 |
82 if(depth >= max) { |
82 if(depth >= max) { |
83 // We can start a free fall now |
83 // We can start a free fall now |
84 // |
84 // |
85 // TODO it should be set inAir if it falls from a cliff |
85 // TODO it should be set inAir if it falls from a cliff |
86 //this->inAir = true; |
86 //this->inAir = true; |
87 |
87 |
88 // Put some speed there to make loke smoother |
88 // Put some speed there to make loke smoother |
89 //this->velocity.y = -5; |
89 //this->velocity.y = -5; |
90 return reached; |
90 return reached; |
91 } |
91 } |
92 |
92 |
93 cursor.y++; |
93 cursor.y++; |
94 if(world.getType(cursor) == EMPTY) { |
94 if(world.getType(cursor) == EMPTY) { |
95 // Check that the other parts of the worm don't collide with anything |
95 // Check that the other parts of the worm don't collide with anything |
96 if(possibleLocation(cursor)) { |
96 if(possibleLocation(cursor)) { |
97 reached = cursor; |
97 reached = cursor; |
98 continue; |
98 continue; |
99 } else { |
99 } else { |
100 // Can't get any further |
100 // Can't get any further |
101 return reached; |
101 return reached; |
102 } |
102 } |
103 } |
103 } |
104 } |
104 } |
105 } |
105 } |
106 |
106 |
107 // cursor.x += right ? 1 : -1; |
107 // cursor.x += right ? 1 : -1; |
108 //} |
108 //} |
109 return reached; |
109 return reached; |
110 } |
110 } |
111 |
111 |
112 void PhysicsObject::jump () { |
112 void PhysicsObject::jump () { |
113 velocity.y = -100; |
113 velocity.y = -100; |
114 inAir = true; |
114 inAir = true; |
115 } |
115 } |
116 |
116 |
117 bool PhysicsObject::possibleLocation (Vector loc) { |
117 bool PhysicsObject::possibleLocation (Vector loc) { |
118 for(unsigned int i = 0; i < this->shape.size(); i++) { |
118 for(unsigned int i = 0; i < this->shape.size(); i++) { |
119 if(world.getType(loc+shape[i]) != EMPTY) |
119 if(world.getType(loc+shape[i]) != EMPTY) |
120 return false; |
120 return false; |
121 } |
121 } |
122 return true; |
122 return true; |
123 } |
123 } |
124 |
124 |
125 /** |
125 /** |
126 * Updates object speed and position. This function organises force |
126 * Updates object speed and position. This function organises force |
127 * integration and collision detection. |
127 * integration and collision detection. |
168 bool collided = false; |
168 bool collided = false; |
169 |
169 |
170 //goes 1 unit forward every step and check if has hit anything |
170 //goes 1 unit forward every step and check if has hit anything |
171 Vector unitVector = (newPosition-position) / (newPosition-position).length(); |
171 Vector unitVector = (newPosition-position) / (newPosition-position).length(); |
172 |
172 |
173 Vector tmpVector = position; |
173 Vector tmpVector = position; |
174 Vector reached = position; |
174 Vector reached = position; |
175 int steps = (int) (newPosition-position).length() + 2; |
175 int steps = (int) (newPosition-position).length() + 2; |
176 |
176 |
177 //Engine::log(DEBUG, "physics.update_position") << unitVector-newPosition; |
177 //Engine::log(DEBUG, "physics.update_position") << unitVector-newPosition; |
178 //Vector foo = position+unitVector*steps-newPosition; |
178 //Vector foo = position+unitVector*steps-newPosition; |
179 //Engine::log(DEBUG, "PhysicsObject.updatePosition") << "Virhe: "<< foo; |
179 //Engine::log(DEBUG, "PhysicsObject.updatePosition") << "Virhe: "<< foo; |
180 |
180 |
181 for(int i = 0; i < steps; i++) { |
181 for(int i = 0; i < steps; i++) { |
182 tmpVector += unitVector; |
182 tmpVector += unitVector; |
183 |
183 |
184 float minVelocity = 10; |
184 float minVelocity = 10; |
185 // Check if any of the four corners of the worm collide |
185 // Check if any of the four corners of the worm collide |
186 for(int sh = 0; sh < 4; sh++) { |
186 for(int sh = 0; sh < 4; sh++) { |
187 if(world.getType(tmpVector+shape[sh]) != EMPTY) { |
187 if(world.getType(tmpVector+shape[sh]) != EMPTY) { |
188 reached = position + unitVector*(i-1); |
188 reached = position + unitVector*(i-1); |
189 collided = true; |
189 collided = true; |
190 this->bounce(world.getNormal(tmpVector+shape[sh], tmpVector-unitVector+shape[sh])); |
190 this->bounce(world.getNormal(tmpVector+shape[sh], tmpVector-unitVector+shape[sh])); |
191 this->velocity *= 0.4; |
191 this->velocity *= 0.4; |
192 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) { |
192 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) { |
193 this->inAir = false; |
193 this->inAir = false; |
194 this->velocity = Vector(0,0); |
194 this->velocity = Vector(0,0); |
195 } |
195 } |
196 break; |
196 break; |
197 } |
197 } |
198 } |
198 } |
199 if(collided) |
199 if(collided) |
200 break; |
200 break; |
201 /* |
201 /* |
202 if(velocity.y > 0) { |
202 if(velocity.y > 0) { |
203 if(world.getType(tmpVector+shape[2]) != EMPTY) { |
203 if(world.getType(tmpVector+shape[2]) != EMPTY) { |
204 reached = position + unitVector*(i-1); |
204 reached = position + unitVector*(i-1); |
205 collided = true; |
205 collided = true; |
206 this->velocity.y *= -0.3; |
206 this->velocity.y *= -0.3; |
207 this->velocity.x *= 0.7; |
207 this->velocity.x *= 0.7; |
208 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) { |
208 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) { |
209 this->inAir = false; |
209 this->inAir = false; |
210 this->velocity = Vector(0,0); |
210 this->velocity = Vector(0,0); |
211 } |
211 } |
212 break; |
212 break; |
213 } |
213 } |
214 } else { |
214 } else { |
215 if(world.getType(tmpVector+shape[0]) != EMPTY) { |
215 if(world.getType(tmpVector+shape[0]) != EMPTY) { |
216 reached = position + unitVector*(i-1); |
216 reached = position + unitVector*(i-1); |
217 collided = true; |
217 collided = true; |
218 this->velocity.y *= -0.3; |
218 this->velocity.y *= -0.3; |
219 this->velocity.x *= 0.7; |
219 this->velocity.x *= 0.7; |
220 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) { |
220 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) { |
221 this->inAir = false; |
221 this->inAir = false; |
222 this->velocity = Vector(0,0); |
222 this->velocity = Vector(0,0); |
223 } |
223 } |
224 break; |
224 break; |
225 } |
225 } |
226 } |
226 } |
227 |
227 |
228 if(velocity.x > 0) { |
228 if(velocity.x > 0) { |
229 if(world.getType(tmpVector+shape[1]) != EMPTY) { |
229 if(world.getType(tmpVector+shape[1]) != EMPTY) { |
230 reached = position + unitVector*(i-1); |
230 reached = position + unitVector*(i-1); |
231 collided = true; |
231 collided = true; |
232 this->velocity.x *= -0.6; |
232 this->velocity.x *= -0.6; |
233 this->velocity.y *= 0.7; |
233 this->velocity.y *= 0.7; |
234 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) { |
234 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) { |
235 this->inAir = false; |
235 this->inAir = false; |
236 this->velocity = Vector(0,0); |
236 this->velocity = Vector(0,0); |
237 } |
237 } |
238 break; |
238 break; |
239 } |
239 } |
240 } else { |
240 } else { |
241 if(world.getType(tmpVector+shape[3]) != EMPTY) { |
241 if(world.getType(tmpVector+shape[3]) != EMPTY) { |
242 reached = position + unitVector*(i-1); |
242 reached = position + unitVector*(i-1); |
243 collided = true; |
243 collided = true; |
244 this->velocity.x *= -0.6; |
244 this->velocity.x *= -0.6; |
245 this->velocity.y *= 0.7; |
245 this->velocity.y *= 0.7; |
246 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) { |
246 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) { |
247 this->inAir = false; |
247 this->inAir = false; |
248 this->velocity = Vector(0,0); |
248 this->velocity = Vector(0,0); |
249 } |
249 } |
250 break; |
250 break; |
251 } |
251 } |
252 }*/ |
252 }*/ |
253 |
253 |
254 // This col. det. doesn't let worms inside the ground, but on the other hand the worms get often stuck |
254 // This col. det. doesn't let worms inside the ground, but on the other hand the worms get often stuck |
255 /*if(world.getType(tmpVector+shape[0]) != EMPTY || (world.getType(tmpVector+shape[2]) != EMPTY)) { |
255 /*if(world.getType(tmpVector+shape[0]) != EMPTY || (world.getType(tmpVector+shape[2]) != EMPTY)) { |
256 reached = position + unitVector*(i-1); |
256 reached = position + unitVector*(i-1); |
257 collided = true; |
257 collided = true; |
258 this->velocity.y *= -0.3; |
258 this->velocity.y *= -0.3; |
259 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) { |
259 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) { |
260 this->inAir = false; |
260 this->inAir = false; |
261 this->velocity = Vector(0,0); |
261 this->velocity = Vector(0,0); |
262 } |
262 } |
263 break; |
263 break; |
264 } |
264 } |
265 if(world.getType(tmpVector+shape[1]) != EMPTY || (world.getType(tmpVector+shape[3]) != EMPTY)) { |
265 if(world.getType(tmpVector+shape[1]) != EMPTY || (world.getType(tmpVector+shape[3]) != EMPTY)) { |
266 reached = position + unitVector*(i-1); |
266 reached = position + unitVector*(i-1); |
267 collided = true; |
267 collided = true; |
268 this->velocity.x *= -0.6; |
268 this->velocity.x *= -0.6; |
269 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) { |
269 if(abs(this->velocity.x) < minVelocity && (abs(this->velocity.y) < minVelocity)) { |
270 this->inAir = false; |
270 this->inAir = false; |
271 this->velocity = Vector(0,0); |
271 this->velocity = Vector(0,0); |
272 } |
272 } |
273 break; |
273 break; |
274 }*/ |
274 }*/ |
275 |
275 |
276 //Engine::log(DEBUG, "physics.update_position") << "didnt hit"; |
276 //Engine::log(DEBUG, "physics.update_position") << "didnt hit"; |
277 } |
277 } |
278 |
278 |
279 // In case of some float error check the final coordinate |
279 // In case of some float error check the final coordinate |
280 if(!collided) { |
280 if(!collided) { |
281 if(world.getType(newPosition+shape[0]) != EMPTY || (world.getType(newPosition+shape[1]) != EMPTY) |
281 if(world.getType(newPosition+shape[0]) != EMPTY || (world.getType(newPosition+shape[1]) != EMPTY) |
282 || (world.getType(newPosition+shape[2]) != EMPTY) || (world.getType(newPosition+shape[3]) != EMPTY)) { |
282 || (world.getType(newPosition+shape[2]) != EMPTY) || (world.getType(newPosition+shape[3]) != EMPTY)) { |
283 |
283 |
284 Engine::log(DEBUG, "physics.update_position") << "didnt hit"; |
284 Engine::log(DEBUG, "physics.update_position") << "didnt hit"; |
285 // There was error, and there is ground |
285 // There was error, and there is ground |
286 //newPosition = tmpVector; |
286 //newPosition = tmpVector; |
287 } else { |
287 } else { |
288 // This means everything was ok, so no need to do anything |
288 // This means everything was ok, so no need to do anything |
289 } |
289 } |
290 } else { |
290 } else { |
324 |
324 |
325 /** |
325 /** |
326 * Computes hitten wall's normal. Calculated from 3*3 grid |
326 * Computes hitten wall's normal. Calculated from 3*3 grid |
327 */ |
327 */ |
328 Vector PhysicsWorld::getNormal (Vector hitPoint, Vector prevPoint) { |
328 Vector PhysicsWorld::getNormal (Vector hitPoint, Vector prevPoint) { |
329 // Search free points with bfs and put them to vector |
329 // Search free points with bfs and put them to vector |
330 std::vector<Vector> frees; |
330 std::vector<Vector> frees; |
331 Vector hit = Vector((int)hitPoint.x, (int)hitPoint.y); |
331 Vector hit = Vector((int)hitPoint.x, (int)hitPoint.y); |
332 Vector prev = Vector((int)prevPoint.x, (int)prevPoint.y); |
332 Vector prev = Vector((int)prevPoint.x, (int)prevPoint.y); |
333 |
333 |
334 assert(hit != prev); |
334 assert(hit != prev); |
335 |
335 |
336 int dirIdx = getDirectionIndex(prev-hit); |
336 int dirIdx = getDirectionIndex(prev-hit); |
337 float tmp1 = hit.x-prev.x; |
337 float tmp1 = hit.x-prev.x; |
338 float tmp2 = hit.y-prev.y; |
338 float tmp2 = hit.y-prev.y; |
339 // Engine::log(DEBUG, "physics.getNormal ") << dirIdx << " " << tmp1 << " " << tmp2; |
339 // Engine::log(DEBUG, "physics.getNormal ") << dirIdx << " " << tmp1 << " " << tmp2; |
340 |
340 |
341 for(int i = 1; i <= 2; i++) { |
341 for(int i = 1; i <= 2; i++) { |
342 if(getType(hit+DIRECTIONS[(dirIdx+i) % 8]) == EMPTY) |
342 if(getType(hit+DIRECTIONS[(dirIdx+i) % 8]) == EMPTY) |
343 frees.push_back(DIRECTIONS[(dirIdx+i) % 8]); |
343 frees.push_back(DIRECTIONS[(dirIdx+i) % 8]); |
344 else |
344 else |
345 break; |
345 break; |
346 } |
346 } |
347 for(int i = 1; i <= 2; i++) { |
347 for(int i = 1; i <= 2; i++) { |
348 if(getType(hit+DIRECTIONS[(dirIdx-i+8) % 8]) == EMPTY) |
348 if(getType(hit+DIRECTIONS[(dirIdx-i+8) % 8]) == EMPTY) |
349 frees.push_back(DIRECTIONS[(dirIdx-i+8) % 8]); |
349 frees.push_back(DIRECTIONS[(dirIdx-i+8) % 8]); |
350 else |
350 else |
351 break; |
351 break; |
352 } |
352 } |
353 frees.push_back(DIRECTIONS[dirIdx]); |
353 frees.push_back(DIRECTIONS[dirIdx]); |
354 |
354 |
355 Vector normal(0,0); |
355 Vector normal(0,0); |
356 for(unsigned int i = 0; i < frees.size(); i++) { |
356 for(unsigned int i = 0; i < frees.size(); i++) { |
357 normal += frees[i]; |
357 normal += frees[i]; |
358 } |
358 } |
359 Engine::log(DEBUG, "physics.getNormal ") << "normal: " << normal; |
359 Engine::log(DEBUG, "physics.getNormal ") << "normal: " << normal; |
360 return normal; |
360 return normal; |
361 } |
361 } |
362 |
362 |
363 /** |
363 /** |
364 * Bounces from straight wall in any direction. |
364 * Bounces from straight wall in any direction. |
365 * Direction given as normal of that wall |
365 * Direction given as normal of that wall |
516 |
516 |
517 /** |
517 /** |
518 * Returns terrainType in given tile. ROCK if tile is out of area |
518 * Returns terrainType in given tile. ROCK if tile is out of area |
519 * @param pos - coordinate of tile |
519 * @param pos - coordinate of tile |
520 */ |
520 */ |
|
521 TerrainType PhysicsWorld::getType(int x, int y) const { |
|
522 if(x < 0 || y < 0 || x >= dimensions.x || y >= dimensions.y) { |
|
523 return ROCK; |
|
524 } |
|
525 return terrain[x][y]; |
|
526 } |
521 TerrainType PhysicsWorld::getType(Vector pos) const { |
527 TerrainType PhysicsWorld::getType(Vector pos) const { |
522 int x = (int)(pos.x); |
528 int x = (int)(pos.x); |
523 int y = (int)(pos.y); |
529 int y = (int)(pos.y); |
524 if(x < 0 || y < 0 || x >= dimensions.x || y >= dimensions.y) { |
530 return getType(x, y); |
525 return ROCK; |
531 } |
526 } |
532 |
527 return terrain[x][y]; |
533 /** |
528 } |
534 * Removes ground from given circle. ROCK is not removed. |
|
535 * @param (x, y) or pos - center of circle. |
|
536 * @param r - radius of circle |
|
537 */ |
|
538 void PhysicsWorld::removeGround(int x, int y, float r) { |
|
539 for(int i = x-(int)r; i < x+r; i++) { |
|
540 for(int j = y-(int)r; j < y+r; j++) { |
|
541 if(getType(i, j) != ROCK) { |
|
542 if((i-x)*(i-x)+(j-y)*(j-y) < r*r) { |
|
543 // Safe because getType returns ROCK if tile is out of bounds |
|
544 terrain[i][j] = EMPTY; |
|
545 } |
|
546 } |
|
547 } |
|
548 } |
|
549 } |
|
550 void PhysicsWorld::removeGround(Vector pos, float r) { |
|
551 removeGround((int)pos.x, (int)pos.y, r); |
|
552 } |