我正在使用处理3.5.3制作小行星游戏。如您所见,碰撞检测非常容易出错。当它检测到船/小行星之间的碰撞时,有时会大于小行星的大小,有时会小于小行星。同样,当小行星变小时,碰撞检测器似乎仍在呼叫更大尺寸的小行星。子弹与小行星之间的碰撞似乎只有在子弹位于小行星的中心时才受到打击。
对所有评论表示歉意,这是我的内部文档所必需的。
这是我的代码,它分为几类。
船
class Ship {
PVector shipAcceleration;
PVector shipVelocity;
PVector shipPosition;
PShape shipShape;
float shipDirection;
int shipLastFire; //holds the time in millis that the last bullet was fired
int shipDelayTime;
Ship() {
shipAcceleration = new PVector();
shipVelocity = new PVector();
shipPosition = new PVector(width/2, height/2); // player starts in middle of screen
shipDirection = 0; // set to 0 to so "up" is a sense of direction
shipLastFire = 0;
shipDelayTime = 300;
keys = new boolean[5];
shipShape = createShape();
shipShape.beginShape();
shipShape.fill(255, 0, 0);
shipShape.vertex(0, -4);
shipShape.vertex(2, 0);
shipShape.vertex(2, 2);
shipShape.vertex(0, 1);
shipShape.vertex(-2, 2);
shipShape.vertex(-2, 0);
shipShape.vertex(0, -4);
shipShape.endShape();
}
void moveShip() {
shipShape.resetMatrix();
// reset.Matrix sourced from https://processing.org/reference/resetMatrix_.html
shipShape.rotate(radians(shipDirection)); // rotates ship
shape(shipShape, shipPosition.x, shipPosition.y, 10, 10);
}
void updateShip() {
// motion sourced from Chapter 22 of 'Processing: A programming handbook
// for visual designers and asrtists' by Casey Reas and Ben Fry
shipAcceleration.x = 0;
shipAcceleration.y = 0;
if (keys[0]) {
shipAcceleration.x = 0.5 * cos(radians(shipDirection) - PI/2);
shipAcceleration.y = 0.5 * sin(radians(shipDirection) - PI/2);
}
if (keys[1] && !keys[2])
{
shipDirection -= 5;
}
if (keys[2] && !keys[1])
{
shipDirection += 5;
}
shipVelocity.add(shipAcceleration);
// add sourced from https://processing.org/reference/PVector_add_.html
shipPosition.add(shipVelocity);
shipVelocity.mult(.95);
// mult sourced from https://processing.org/reference/PVector_mult_.html
shipPosition.x %= width;
if (shipPosition.x < -10)
{
shipPosition.x = width;
}
shipPosition.y %= height;
if (shipPosition.y < -10)
{
shipPosition.y = height;
}
if (keys[4]) {
if (millis() - shipLastFire > shipDelayTime) {
shipLastFire = millis();
fireBullet(shipPosition, shipVelocity, shipDirection);
}
}
}
}
子弹
class Bullet {
PVector bulletPosition;
PVector bulletVelocity;
boolean bulletHidden; // used if lifespan is max and to help recycle
int bulletSize;
int bulletCreationTime;
int bulletLifespan; //the time in milli seconds that bullets last
int bulletSpeed;
Bullet() {
bulletHidden = true;
bulletSize = 5;
bulletPosition = new PVector();
bulletVelocity = new PVector();
bulletCreationTime = 0;
bulletLifespan = 3000;
bulletSpeed = 5;
}
void updateBullet() {
if (!bulletHidden) {
bulletPosition.add(bulletVelocity);
if (millis() - bulletCreationTime > bulletLifespan)
// millis sourced from https://processing.org/reference/millis_.html
{
bulletHidden = true;
}
bulletPosition.x %= width;
if (bulletPosition.x < -1)
{
bulletPosition.x = width;
}
bulletPosition.y %= height;
if (bulletPosition.y < -1)
{
bulletPosition.y = height;
}
}
}
void drawBullet() {
if (!bulletHidden) {
updateBullet();
ellipse(bulletPosition.x, bulletPosition.y, bulletSize, bulletSize);
}
}
void reset(PVector pos, PVector spe, float direct) {
bulletPosition = new PVector(pos.x + (20 * cos(radians(direct) - PI/2)), pos.y + (20 * sin(radians(direct) - PI/2)));
bulletVelocity.x = bulletSpeed * cos(radians(direct) - PI/2) + spe.x;
bulletVelocity.y = bulletSpeed * sin(radians(direct) - PI/2) + spe.y;
bulletCreationTime = millis();
bulletHidden = false;
}
}
小行星
class Asteroid {
float asteroidSize = (width/80*12);
float x;
float y;
float velocityX;
float velocityY;
PVector[] vertices = new PVector[8];
boolean active = true; //false after collision
int level = 1; // how many times has it been shot. Level 1 is not yet shot
Asteroid(float xPos, float yPos, int aLevel) {
if (xPos == 0 && yPos == 0) { //if begin level asteroids
x = random(width) ; // set random start positions
y = random (height);
} else { // if collision generating 2 smaller asteroids
x = xPos; // set from asteroid x, y
y = yPos;
}
velocityX = random(-2, 2);
velocityY = random(-2, 2);
level = aLevel; //sets asteroid level (how many times shot)
//create polygon. /aLevel generates smaller polygons with each collision.
vertices[0] = new PVector(random (width/80*3/aLevel), random(height/80*3/aLevel) );
vertices[1] = new PVector(random((width/80*4/aLevel), (width/80*8/aLevel)), random(height/80*3/aLevel) );
vertices[2] = new PVector(random((width/80*9/aLevel), (width/80*12/aLevel)), random(height/80*3/aLevel) );
vertices[3] = new PVector(random((width/80*9/aLevel), (width/80*12/aLevel)), random((height/80*4/aLevel), (height/80*8/aLevel)) );
vertices[4] = new PVector(random((width/80*9/aLevel), (width/80*12/aLevel)), random((height/80*9/aLevel), (height/80*12/aLevel)) );
vertices[5] = new PVector(random((width/80*4/aLevel), (width/80*8/aLevel)), random((height/80*9/aLevel), (height/80*12/aLevel)) );
vertices[6] = new PVector(random(width/80*3/aLevel), random((height/80*9/aLevel), (height/80*12/aLevel)) );
vertices[7] = new PVector(random(width/80*3/aLevel), random((height/80*4/aLevel), (height/80*8/aLevel)) );
}
void moveAsteroid() {
x = x + velocityX; //asteroids to move with a random velocity
y = y + velocityY;
if ( x < -1 * asteroidSize ) {
x = width + asteroidSize;
} //if off screen left, come back in right
if ( x > width + asteroidSize ) {
x = -1 * asteroidSize;
} // if off screen right, come back in left
if ( y < -1 * asteroidSize ) {
y = height + asteroidSize;
} //if off top of screen, come back in bottom
if ( y > height + asteroidSize ) {
y = -1 * asteroidSize ;
} //if off bottom of screen, come back in top
}
void asteroidDraw() {
if (active == false) { // If not active don't draw
return;
}
stroke(150);
fill(255);
// this was how I orginally coded. Have kept commented out for now, so I can see what I did, but will delete before submission.
/*beginShape();
vertex(vertices[0].x, vertices[0].y );
vertex(vertices[1].x, vertices[1].y );
vertex(vertices[2].x, vertices[2].y );
vertex(vertices[3].x, vertices[3].y );
vertex(vertices[4].x, vertices[4].y );
vertex(vertices[5].x, vertices[5].y );
vertex(vertices[6].x, vertices[6].y );
vertex(vertices[7].x, vertices[7].y );
endShape(CLOSE); */
beginShape();
for (PVector v : vertices) {
vertex(x+v.x, y+v.y);
}
endShape(CLOSE);
}
void manDown() {
active = false; //sets to in active so will stop drawing
// add 2 new asteroids to array
asteroids = (Asteroid[]) append( asteroids, new Asteroid( x+20, y+20, level + 1 ) ); // Appends asteroid to array. Changing level makes the asteroid smaller.
asteroids = (Asteroid[]) append( asteroids, new Asteroid( x-20, y-20, level + 1 ) ); // Appends two smaller asteroids to array.
}
}
游戏管理员
class GameManager {
int scoreCount;
boolean gameState = true;
int lifeCount;
void newGame()
{
gameState = true; //sets game state to in play
scoreCount = 0; //set counter of flies killed to 0
lifeCount = 3;
}
void scoreUpdate()
{
textSize(width*3/100);
textAlign(LEFT);
fill(255);
text("Score " + scoreCount, (width*2/100), (height*4/100));
}
void lifeLost()
{
lifeCount = lifeCount - 1;
if (lifeCount <= 0) {
gameState = false;
gameOver();
}
}
void lifeUpdate()
{
textSize(height*3/100);
textAlign(LEFT);
fill(255);
text("Lives " + lifeCount, (width*2/100), ((height*4/100) + (height*3/100)) );
}
void gameOver()
{
background(0);
textSize(height*5/100);
textAlign(CENTER);
fill(255);
text("Game over", width/2, height/2.6);
//play again button
fill(255);
rect(((width/2)-(width/4)), (((height/2)- (height/12))), width/2, height/8);
fill(0);
text("Play Again", width/2, height/2);
//define area for play again button collision
if (mousePressed)
{
if (
(mouseX > width/4) &&
(mouseX < width/4 +width/2) &&
(mouseY > (height/2-height/10.5)) &&
(mouseY < ((height/2-height/10.5) + height/8))
)
{
setup(); //reset game
}
}
}
}
主要
Asteroid[] asteroids; //K Level 1 starts with 6, add 2 each level, 10 levels
Ship myShip;
GameManager gameManager;
ArrayList<Bullet> bullets;
// Array help sourced from chapter 28 of 'Processing: A programming handbook
// for visual designers and asrtists' by Casey Reas and Ben Fry
int bulletIndex; // used to recycle bullets
// index sourced from https://py.processing.org/reference/list_index.html
int startNum = 6; //K begin game with 6 asteroids in the level
boolean[] keys; // boolean for storing keypressed/released
void setup() {
size(800, 800);
gameManager = new GameManager();
gameManager.newGame();
bulletIndex = 0;
bullets = new ArrayList<Bullet>();
keys = new boolean[5];
myShip = new Ship();
asteroids = new Asteroid [startNum]; //K
for (int a = 0; a < startNum; a++) { //K create asteroids in array
asteroids[a] = new Asteroid(0, 0, 1); //K
}
for (int i = 0; i < 20; i++)
{
bullets.add(new Bullet()); // create bullets
}
}
void draw() {
background(0);
collisionDetect();
gameManager.gameState = true;
myShip.updateShip(); // E
myShip.moveShip(); // E
for (int a = 0; a < asteroids.length; a++) { //K for asteroids in array
asteroids[a].moveAsteroid(); //K
asteroids[a].asteroidDraw(); //K
}
gameManager.scoreUpdate();
gameManager.lifeUpdate();
for (int i = 0; i < bullets.size(); i++)
{
bullets.get(i).drawBullet(); // drawing bullets
}
}
void keyPressed() {
if (key == CODED) {
if (keyCode == UP)
keys[0] = true;
if (keyCode == LEFT)
keys[1] = true;
if (keyCode == RIGHT)
keys[2] = true;
if (keyCode == DOWN)
keys[3] = true;
} else {
if (key == 'w')
keys[0] = true;
if (key == 'a')
keys[1] = true;
if (key == 'd')
keys[2] = true;
if (key == 's')
keys[3] = true;
if (key == ' ')
keys[4] = true;
}
}
void keyReleased() {
if (key == CODED) {
if (keyCode == UP)
keys[0] = false;
if (keyCode == LEFT)
keys[1] = false;
if (keyCode == RIGHT)
keys[2] = false;
if (keyCode == DOWN)
keys[3] = false;
} else {
if (key == 'w')
keys[0] = false;
if (key == 'a')
keys[1] = false;
if (key == 'd')
keys[2] = false;
if (key == 's')
keys[3] = false;
if (key == ' ')
keys[4] = false;
}
}
void fireBullet(PVector pos, PVector spe, float dir) {
bullets.get(bulletIndex).reset(pos, spe, dir);
// set attributes of last used bullet
// get sourced from https://processing.org/reference/get_.html
bulletIndex++; //update index
bulletIndex %= bullets.size(); //keep index in range
}
void collisionDetect(){
Asteroid testHolder;
Bullet bulletHolder;
// asteroid and bullet objects to minimize creating new objects
for(int i = 0; i < asteroids.length; i++){
testHolder = asteroids[i];
if(dist(testHolder.x, testHolder.y, myShip.shipPosition.x,
myShip.shipPosition.y) < testHolder.asteroidSize)
// collision of player and the asteroid
{gameManager.gameOver();}
for(int j = 0; j < bullets.size(); j++){
bulletHolder = bullets.get(j);
// pull and store each bullet from the list
if(bulletHolder.bulletHidden){continue;}
// don't calculate anything if it is hidden
if(dist(testHolder.x, testHolder.y, bulletHolder.bulletPosition.x,
bulletHolder.bulletPosition.y) < testHolder.asteroidSize){
testHolder.manDown();
// used to detect collision and split if collided
bulletHolder.bulletHidden = true;
// hide the bullet so it won't go 'through' the asteroids
j++;
}
}
}
}
答案 0 :(得分:0)
对于较小的小行星的问题,您需要使asteroidSize
取决于水平。当前它们都是相同的:float asteroidSize = (width/80*12);
关于碰撞问题,第一件事是,您还必须考虑撞击小行星的船舶/子弹的尺寸:
if(dist(testHolder.x, testHolder.y, myShip.shipPosition.x, myShip.shipPosition.y) < (testHolder.asteroidSize + myShip.size))
为了清楚起见:在两种情况下,尺寸均为半径。
第二,由于您的形状不是圆形,因此总会有某些区域的基本类型的碰撞检测不遵循视觉。您在小行星上使用的随机性在这方面无济于事。对此进行更多控制的一种方法是在每个级别定义几个形状,并在创建小行星时随机选择其中一个形状。这样,您可以调整形状/半径以在外观和功能之间做出良好的权衡,从而使其看起来“足够可信”。