Asteroids - make bullets come from same point in craft each time
So I'm still working on my Asteroids clone in XNA and I've hit another brick wall. How would I ensure that the bullets spawn from the same point on the craft each time (i.e. the front) and ensure that they travel in the direction the craft is facing?
I've got the origin part right (I think, well at least the bullets all start in the same place) and got the bullets kind of fanning out, but the angles are wrong. When the craft is facing directly up i.e. has completed a 0° turn, the bullet goes straight up which seems correct. When the craft is at 90°, it works fine. When the craft is at 180°, the bullet goes up as at 0°, and when facing 270° the bullet goes out in the correct direction.
The rest of the time, the bullet appears to be 45° more or less than the current direction e.g. the craft is pointing south-east and the bullet travels north-east.
I tried setting the bullet's direction to be the same as the craft... nothing.
I tried setting the bullet's rotation value to be the same as the craft... nada. What then happens is things go screwy if you do multiple rotations and the craft has travelled through something like 1223° or if you do multiple anti-clockwise rotations and it's magically done -1412°.
I tried everything else... zip.
The bullets and craft are sprites of type Texture2D, not raw pixels or fancy particle effects.
I don't think I know exactly how to do this, if any of you have written Asteroids could you possibly help me out?
Here's my class representing the bullet:
Here's the class representing the spacecraft:
Here's where we create new bullets and push them into a List<Bullet>. "test" is the instance of the spaceShip object.
There's a lot of clutter and junk in there, the codebase really is a mess I'm afraid to say :S. I hope what I've given you is sufficient.
using System;using System.Collections.Generic;using System.Text;using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Graphics;using Microsoft.Xna.Framework.Content;namespace Asteroids{ class Bullet { private Texture2D bulletTex; private Vector2 bulletOrigin; // The bullet's origin is the same for all bullets, they must spawn from the same part of the spacecraft private Vector2 bulletPosition; private Vector2 bulletDirection; private Vector2 bulletVelocity; private double dFVx; private double dFVy; public Bullet(Game game, SpaceShip ship) { bulletTex = game.Content.Load<Texture2D>("bullet"); bulletOrigin = new Vector2((ship.shipPosition().X), (ship.shipPosition().Y)); bulletDirection = new Vector2((float)Math.Sin(ship.rotation().Z), (float)(Math.Cos(ship.rotation().Z))); bulletVelocity = bulletDirection * 5.0f; //dFVx = Math.Cos(bulletOrigin.X); //dFVy = Math.Sin(bulletOrigin.X); bulletPosition = bulletOrigin; } // Set the bullet's velocity public void setVelocity(float t) { bulletOrigin *= bulletOrigin * t; } // Return the texture we're using for drawing purposes public Texture2D returnTexture() { return (bulletTex); } // Return the vector representing the position public Vector2 returnPosition() { return (bulletPosition); } // Move the bullet public void moveBullet() { bulletPosition += bulletVelocity; } }}
Here's the class representing the spacecraft:
using System;using System.Collections.Generic;using System.Text;using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Content;using Microsoft.Xna.Framework.Graphics;namespace Asteroids{ class SpaceShip { private Texture2D shipTex; public Vector2 shipPos; private Vector2 rotationOrigin; private Vector3 shipRotation; private Vector3 shipAccel; public Vector2 shipVelocity; private Vector3 shipDirection; private int screenWidth; private int screenHeight; private bool currentlyBoosting; // Constructor public SpaceShip(Game game) { shipTex = game.Content.Load<Texture2D>("sp_2"); screenWidth = game.GraphicsDevice.Viewport.Width; screenHeight = game.GraphicsDevice.Viewport.Height; shipPos = new Vector2(screenWidth / 2, screenHeight / 2); rotationOrigin = new Vector2(shipTex.Width / 2, shipTex.Height / 2); shipRotation = new Vector3(MathHelper.ToRadians(90), 0, 0); shipAccel = new Vector3(0.0f, 0.0f, 0.0f); shipVelocity = new Vector2(0.0f, 0.0f); shipDirection = new Vector3((float)(Math.Sin(shipRotation.Z)), (float)(Math.Cos(shipRotation.Z)), 0); currentlyBoosting = false; } // Resets the craft to its original position public void reset() { shipPos = new Vector2(screenWidth / 2, screenHeight / 2); rotationOrigin = new Vector2(shipTex.Width / 2, shipTex.Height / 2); shipRotation = new Vector3(MathHelper.ToRadians(90), 0, 0); shipAccel = new Vector3(0.0f, 0.0f, 0.0f); shipVelocity = new Vector2(0.0f, 0.0f); shipDirection = new Vector3((float)(Math.Sin(shipRotation.Z)), (float)(Math.Cos(shipRotation.Z)), 0); currentlyBoosting = false; } // Moves the spacecraft to a given position public void moveTo(float x, float y) { if (currentlyBoosting) { shipPos.X += (x * 2); shipPos.Y -= (y * 2); } else { shipPos.X += x; shipPos.Y -= y; } } // Tells us if the ship is boosting or not public bool isBoosting() { return (currentlyBoosting); } // Rotates the ship based on the right thumbstick public void rotateShip(float x, float y) { shipRotation.Z += (float)(Math.Atan2(x, y)) / 10; } // Performs a hyperspace jump by simply respawning //public void doHyperJump() //{ // Random newPos = new Random(); // float newX = newPos.Next(50, screenWidth); // float newY = newPos.Next(250, screenHeight); // Vector2 hyperJumpPosition = new Vector2(newX, newY); // shipPos = hyperJumpPosition; // moveTo(newX, newY); //} // The function for turning the ship's boost on // Boosting causes the ship to move as much faster, in the way it's facing, as the left trigger is held down public void boostOn() { currentlyBoosting = true; } // Turn the boost off when we're done // Returns the spaceship to normal speed again public void boostOff() { currentlyBoosting = false; } // Return the ship's texture // Used for drawing it public Texture2D shipTexture() { return (shipTex); } // Return the current ship position vector // Used as the destination rectangle public Vector2 shipPosition() { return (shipPos); } // Return the current x coordinate for the ship public float curX() { return (shipPos.X); } // Return the current y coordinate for the ship public float curY() { return (shipPos.Y); } //// Rotate the ship according to the right thumbstick //public void rotateShip(float degree) //{ // shipRotation.Z = //} // Return the current rotation vector for the ship // Also used in terms of drawing it public Vector3 rotation() { return (shipRotation); } // Return the ship's rotation origin public Vector2 getRotationOrigin() { return (rotationOrigin); } // Return the ship's acceleration vector public Vector3 getAcceleration() { return (shipAccel); } }}
Here's where we create new bullets and push them into a List<Bullet>. "test" is the instance of the spaceShip object.
if (GamePad.GetState(PlayerIndex.One).IsButtonDown(Buttons.RightTrigger)) { // We only want to allow three bullets a second if (currentTime - lastBulletTime > 300) { Bullet local = new Bullet(this, test); local.setVelocity(t); firePower.Add(local); lastBulletTime = currentTime; } }// snip...// This is the code for drawing the bulletsforeach (Bullet bullet in firePower) { bullet.moveBullet(); spriteBatch.Draw(bullet.returnTexture(), bullet.returnPosition(), Color.White); }
There's a lot of clutter and junk in there, the codebase really is a mess I'm afraid to say :S. I hope what I've given you is sufficient.
I don’t understand this function of Bullet:
Isn’t a setVelocity function supposed to set a velocity? Why you use a vector for a rotation angle that is a scalar?
A design note: I think the ship and the bullets shouldn’t know about the texture used to display it. I also think is the system that should set the position of the ship and not the ship itself. What if you want to add a multiplayer mode? You have to create two ships in different positions, but in the SpaceShip constructor you set the position in the center of the screen and the only way to change the position is using the moveTo function.
/ Set the bullet's velocitypublic void setVelocity(float t){ bulletOrigin *= bulletOrigin * t;}
Isn’t a setVelocity function supposed to set a velocity? Why you use a vector for a rotation angle that is a scalar?
A design note: I think the ship and the bullets shouldn’t know about the texture used to display it. I also think is the system that should set the position of the ship and not the ship itself. What if you want to add a multiplayer mode? You have to create two ships in different positions, but in the SpaceShip constructor you set the position in the center of the screen and the only way to change the position is using the moveTo function.
I don't really understand what you mean in terms of a scalar rotation angle? I've changed the rotation to a float from a Vector2 but I don't see how or why it makes a difference.
I've changed the design of my game entirely now, purely because of this problem. The spacecraft is locked to the bottom of the screen and shoots at objects coming down, it's kind of like Space Invaders and Breakout. I've got the spacecraft so it can rotate 60° to left or right, and I'd like it to be able to fire off in those directions depending on where it's pointing.
If only I could get this stuff with the bullets working properly. What happens now is, well, nothing. The bullets don't even move at all.
I think I might have to junk the bullet class and start over, something's not right at all. What do you think, or do you have any suggestions before I do so?
I just don't understand what I'm supposed to be doing. I've thought about it in detail but I really don't have a clue. You'd think it would be as simple as setting the direction of the bullet to the direction the ship's facing... fuck it, I don't understand.
I've changed the design of my game entirely now, purely because of this problem. The spacecraft is locked to the bottom of the screen and shoots at objects coming down, it's kind of like Space Invaders and Breakout. I've got the spacecraft so it can rotate 60° to left or right, and I'd like it to be able to fire off in those directions depending on where it's pointing.
If only I could get this stuff with the bullets working properly. What happens now is, well, nothing. The bullets don't even move at all.
I think I might have to junk the bullet class and start over, something's not right at all. What do you think, or do you have any suggestions before I do so?
I just don't understand what I'm supposed to be doing. I've thought about it in detail but I really don't have a clue. You'd think it would be as simple as setting the direction of the bullet to the direction the ship's facing... fuck it, I don't understand.
There are two advantages in using a scalar instead of a vector for angles:
1. You use less memory.
2. The code is easier to understand (if you use a vector it isn’t clear the purpose of the others value).
A bullet is spawn in front of the ship (something like shipOrigin + shipOrientation*distance) and then the movement is controlled by the following formula:
bulletPosition = bulletOrigin + bulletVelocity*t
Your Bullet class have several problems (like the setVelocity class that set the origin).
1. You use less memory.
2. The code is easier to understand (if you use a vector it isn’t clear the purpose of the others value).
A bullet is spawn in front of the ship (something like shipOrigin + shipOrientation*distance) and then the movement is controlled by the following formula:
bulletPosition = bulletOrigin + bulletVelocity*t
Your Bullet class have several problems (like the setVelocity class that set the origin).
I'm going to hose the bullet class at some point and rewrite it, definitely. It's got into such a mess that it's my only real option.
Here's some psuedo code to get you started.
"""Overview: bullet spawning: b = Bullet() vel = ship.normal_foreward() vel *= SPEED_BULLET_INITIAL; # use your initial bullet speed here vel += ship.vel # if you want to add ship vel to initial vel b.vel = vel b.loc = ship.loc + ship.offset_loc() spawn( b )"""# psuedo code:class Entity(): """base physics entity(). any object that uses location, velocity, and acceleration will derive from this class.""" Vector3 loc; # location Vector3 vel; # velocity Vector3 accel; # acceleration float rotation; #rotation of ship in radians ( or you can convert before sin) def update(self): """physics update""" vel += accel loc += vel accel = Vector2(0,0) # reset accel every frame. def draw(self): # virtual; to be implemented in derived class. def accelerate(self, force): """add force / accelerate. add mass into the calculation if needed.""" self.accel += force; def rotate(self, rotation): self.rotation += rotation; def normal_foreward(self): """get the normal_foreward vector.""" normal = Vector2() normal.x = cos( self.rotation ); normal.y = sin( self.rotation ) normal.normalize() return normal class Bullet(Entity): """bullet class""" def draw(self): # do bullet drawclass Ship(Entity): """player ship. inherits all of Entity()-ies members.""" def draw(self): """do ship draw""" def fire(self): """fire, and spawn bullet.""" b = Bullet() vel = Vector2() """calculate the bullet vel. vel = bullet_initial + ship.vel [ in the direction ship is moving]""" vel = self.normal_foreward(); vel *= SPEED_BULLET_INITIAL; # use your initial bullet speed here vel += self.vel # if you want to add ship vel to initial vel b.vel = vel """calc bullet loc loc = ship.loc + spawn offset""" b.loc = self.loc + self.offset_loc(); """then depending on how you handle your objects, spawn here, or maybe return the new Bullet().""" game.spawn( bullet ) def offset_loc(self): """get the offset to spawn bullet. SHIP_LENGTH = dist from ship center to ship spawn location""" return self.normal_foreward() * SHIP_LENGTH
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement