Skip to main content
GameDev.net gamedev.net
🔒 Locked

Making an image shoot

Started by colts18 Feb 5, 2011 at 8:14 PM 34 replies 9.6k views
Original Post
colts18
colts18
Hey guys I have a tank and I want it to shoot a bullet I created. I want the bullet to continue flying through the air until it hits the enemy tank and the tank explodes. I have an image for that too. Does anyone know how?
colts18
colts18
Here is the code:

import pygame

class Tank:

def __init__(self):
self.tank = pygame.image.load("tank.png")
self.x = -200
self.y = 20
def draw(self, surface):
surface.blit(self.tank, (self.x, self.y))

class Lvl_1:

def __init__(self):
self.floor = pygame.image.load("floor.png")
def draw(self, surface):
surface.blit(self.floor, (0, 0))

class Enemy_tank:

def __init__(self):
self.enemy_tank = pygame.image.load("enemy_tank.png")
def draw(self, surface):
surface.blit(self.enemy_tank, (100, -40))

pygame.init()
background_color = (0,0,0)
(width, height) = (640, 480)
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('Game Window')
clock = pygame.time.Clock()
tank = Tank()
lvl_1 = Lvl_1()
enemy_tank = Enemy_tank()
pygame.key.set_repeat(1,50)
running = True

while running:

screen.fill (background_color)
tank.draw(screen)
lvl_1.draw(screen)
enemy_tank.draw(screen)
pygame.display.flip()
clock.tick(30)

for event in pygame.event.get():


if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
tank.x += 5
if event.key == pygame.K_LEFT:
tank.x -= 5
mrwonko
mrwonko
You could create a new class "Bullet" which represents bullets. It needs some kind of Update() function which moves the bullet forward based on the time since the last frame (new position = old position + time * velocity). You could also check for collisions there (e.g. Bullet hits Enemy_tank).

Does pygame keep track of resource management? If you call pygame.image.load twice for the same image, will it be loaded again (bad) or will you get the same image (good)? If the former is the case, you'll need to avoid duplicate loading yourself, you don't want a new image to be created for each Bullet object.

And your Tank needs a Shoot() function which creates a new Bullet, which is then added to some kind of list so you can keep track of it (e.g. call Update()).


This is a pretty general answer since your question is rather vague, too. Should give you a general idea of how you can do it. (There are many ways of doing it.) If you need more specific information, ask more specific questions.

So long,
Mr. Wonko
EndersGames
EndersGames
Back again eh?

Here is a nice link to some python game programming examples.

I used these when I was first learning.

http://eli.thegreenplace.net/2008/12/13/writing-a-game-in-python-with-pygame-part-i/

The biggest thing you should figure out is to learn to think "how do I do something"

I learned a while ago, that when i wanted something to happen in a game, I would write down everything that I wanted to happen. For example:

* I want a bullet to shoot from my tank
* It will shoot to the right
* It's speed will be 10 pixels per frame
* Gravity will not effect it
* When it hits something, the bullet will go away.
* If it does not hit something, and goes off screen, the bullet will go away.
* There can only be 1 bullet on screen at a time.

This is a nice simple list of the characteristics of a bullet. It will give me rules to follow. Now I will have to try and it and create code for the bullet. The bullet is a bit more dynamic the the tank as it requires more rules in game. But we can start with a class to give it it's image and x, y location. Probably something where the bullet.y is equal to tank.y and its x is offsetted some.

You will probably need to give the bullet a boolean for being on screen.

And maybe another boolean for on hit.

Now in your main loop, if space bar is press, you can have it activate a function in the bullet class to activate the on screen flag, and change its x and y position to be near your tank.

You will want to make it so that it will only do that if the on screen flag is set to false.

Also in the main loop, right behind the draw function for the bullet, you can have an update position function, That will increase it's x position 10 to the right.

After that call, you can have another function to check its position compared to the end of the screen and see if it will collide with another tank. If the check is true, then you can change the on hit flag to true. So now when the draw function is called, the on hit flag will show an explosion picture instead of a bullet picture. After the picture is shown, you change the on hit and on screen flag to false.

It is pretty rudimentary. But take a look at that link i provided. It is pretty nice.

Good luck!
colts18
colts18
Ok I started to create the bullet class but I'm still not sure how to add the spacebar input in the main loop.
colts18
colts18
This is the partially completed bullet class is there some special way to draw the bullet since I don't want to draw it until the space bar is pressed?

class Bullet:

def __init__(self):
self.bullet = pygame.image.load("tank_bullet.png")
self.speed = 10
Fl4sh
Fl4sh

This is the partially completed bullet class is there some special way to draw the bullet since I don't want to draw it until the space bar is pressed?

class Bullet:

def __init__(self):
self.bullet = pygame.image.load("tank_bullet.png")
self.speed = 10




Something like:

1. Wait for the spacebar keydown event
a.Get position of the person or ship
b. From that point, have it travel as you want
c. use a bool to determine if it has hit something.
They hated on Jeezus, so you think I give a f***?!
colts18
colts18
I'm having trouble getting it to shoot I'm not completely sure if I'm doing it right can someone tell me if I am and if not what am I doing wrong? This is what I have so far.

class Bullet:

def __init__(self):
self.bullet = pygame.image.load("tank_bullet.png")
self.speed = 10
self.y = tank.y
self.x = tank.x
def draw(self, surface):
if event.key == pygame.K_SPACE:
surface.blit(self.bullet, (self.x, self.y))
Burnt_Fyr
Burnt_Fyr

I'm having trouble getting it to shoot I'm not completely sure if I'm doing it right can someone tell me if I am and if not what am I doing wrong? This is what I have so far.

class Bullet:

def __init__(self):
self.bullet = pygame.image.load("tank_bullet.png")
self.speed = 10
self.y = tank.y
self.x = tank.x
def draw(self, surface):
if event.key == pygame.K_SPACE:
surface.blit(self.bullet, (self.x, self.y))



I've been following this thread, and your previous. To be honest, I don't think you have what it takes at this point in time, to accomplish what you are trying to do. You need to understand what is going on with your code, and not just blindly copy peoples suggestions here. You will not get anywhere, and eventually those who are helping will grow tired of your neediness. I'm sorry it just needs to be said. With that out of the way, let's look at POSSIBLE solutions.

You were given a *great* leg up by Enders, who went though the process of design, rather than just providing an answer. You ran with it and got stuck. These things happen, don't let it get you down. Fl4sh provided a general solution, and you tried again, resulting in your current code. Looking at that, your saying that if your drawing, and the space key is pressed, then blit the surface. Does this sound like what you want to have happen? Should the bullet itself be firing itself?

Let's move back a sec and look at the tank class, it has a method to draw itself, but that is it. doesn't a tank do more? lets add 2 methods to tank. Fire, and update. Fire is where we will handle the creating of a bullet, and update will be a method that will allow us to handle updates to the tank. Once again i've no experience with python, so I have no idea if this syntax is correct, but hopefully you will understand enough to make it work


def update(time) :
// the time variable allows us to watch time elapsed for things like reloading ammo, special moves, etc, but if youd don't need it it's fine to leave it out for now
if event.Key == pygame.K_SPACE:
self.fire()

sorry for the c style comments, hope you can see what's going on there. No code, just a design template for you to work with. so the tank will now fire if the space key is down during it's update.


def fire():
// create a bullet
//set the bullets position to the tanks position
// give the bullet a direction
// add the bullet to a list of bullets

So the fire method will create, and initialize the bullet object so it is ready to go


def update(time) :
self.x += self.xdirection *self.speed * time
self.y += self.ydirection * self.speed * time

This update method will fit in the bullet class. basically every update, it moves a distance along it's direction, which is it's speed * time elapsed.

Now we have a method for creating a bullet, a method for updating it, and a method to update the tank. your main loops will now look something like


while running

//update game state
tank.update(time) // you will need to get how much time has elapsed since last frame
enemy_tank.update(time)
foreach(bullet in bulletlist)
{
bullet.update()
}

// gamestate is updated, so lets draw it

tank.draw(screen)
lvl_1.draw(screen)
enemy_tank.draw(screen)
pygame.display.flip()
clock.tick(30)
colts18
colts18
This is what I tried for the tank and bullet classes but it still won't shoot:

class Tank:

def __init__(self):
self.tank = pygame.image.load("tank.png")
self.x = -200
self.y = 20
def draw(self, surface):
surface.blit(self.tank, (self.x, self.y))
def update(time):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
self.fire()

class Bullet:

def fire():
self.bullet = pygame.image.load("tank_bullet.png")
self.y = tank.y
self.x = tank.x
self.speed = 10
right = self.x + 5
def update(time):
self.x += self.x, right * self.speed * 30
self.y += self.y, right * self.speed * 30
Fl4sh
Fl4sh
Doesn't pygame have bounding rectangles and stuff? ;o
They hated on Jeezus, so you think I give a f***?!
Burnt_Fyr
Burnt_Fyr
The tank FIRES the bullet... the bullet doesn't fire itself...
colts18
colts18
Ok I moved the fire() to the tank but still not shooting

class Tank:

def __init__(self):
self.tank = pygame.image.load("tank.png")
self.x = -200
self.y = 20
def draw(self, surface):
surface.blit(self.tank, (self.x, self.y))
def update(time):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
self.fire()
def fire():
self.bullet = pygame.image.load("tank_bullet.png")
self.y = tank.y
self.x = tank.x
self.speed = 10
right = self.x + 5
def update(time):
self.x += self.x, right * self.speed * 30
self.y += self.y, right * self.speed * 30
Fl4sh
Fl4sh
Colts like someone else said, you can't just mindlessly copy/paste peoples suggestions and expect it to work. ohmy.gif

You have to understand what's going on with the code... at-least to some degree. unsure.gif
They hated on Jeezus, so you think I give a f***?!
DarklyDreaming
DarklyDreaming
I think you are confused. Fire() should create an instance of Bullet which in turn needs to be updated (by calling .Update()). Load the image in the __init__() method of Bullet. After all, you DON'T want that image loaded everytime someone presses fire, now do you? You want every Bullet instance to have that image, which logically should then be loaded upon everytime a Bullet is created (actually, preferably you would load the image once somewhere else but let's not get deeper into that right now..)

Take a deep breath. Plan things out in pseudo-code first. It'll make it so much easier.


Class Tank
Methods:
__init__
Draw
Update
Fire

Class Bullet
Methods:
__init__
Draw
Update



Of course, then there is the matter of how this all plugs into the game. How do you keep track of the bullets the tank fires? (Maybe with a list? A vector? Array?). Then you need to call Update() in your game's Update method, and Draw in your Draw method and so on. Good luck!
"I will personally burn everything I've made to the fucking ground if I think I can catch them in the flames."
~ Gabe

"I don't mean to rush you but you are keeping two civilizations waiting!"
~ Cavil, BSG.
"If it's really important to you that other people follow your True Brace Style,
colts18
colts18
Ok I still don't fully understand the fire() but I tried, it still doesn't work but is this closer?

class Tank:

def __init__(self):
self.tank = pygame.image.load("tank.png")
self.x = -200
self.y = 20
def draw(self, surface):
surface.blit(self.tank, (self.x, self.y))
def update():
self.tank.Update()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
self.fire()
def fire():
if self.fire():
__init__(bullet)


class Bullet:
def __init__(self):
self.bullet = pygame.image.load("tank_bullet.png")
self.y = tank.y
self.x = tank.x
self.speed = 10
right = self.x + 5
def draw(self, surface):
surface.blit(self.x, self.y)
def update():
self.bullet.Update()
self.x += self.x, right * self.speed * 30
self.y += self.y, right * self.speed * 30


I am also starting to think there could be a problem in the main loop:

pygame.init()
background_color = (0,0,0)
(width, height) = (640, 480)
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('Game Window')
clock = pygame.time.Clock()
tank = Tank()
lvl_1 = Lvl_1()
bullet = Bullet()
enemy_tank = Enemy_tank()
pygame.key.set_repeat(1,50)
running = True

while running:

screen.fill (background_color)
tank.draw(screen)
lvl_1.draw(screen)
enemy_tank.draw(screen)
pygame.display.flip()
clock.tick(30)

for event in pygame.event.get():


if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
tank.x += 5
if event.key == pygame.K_LEFT:
tank.x -= 5
Burnt_Fyr
Burnt_Fyr

Ok I still don't fully understand the fire() but I tried, it still doesn't work but is this closer?

class Tank:

def __init__(self):
self.tank = pygame.image.load("tank.png")
self.x = -200
self.y = 20
self.firecountdown=0 # this will make sure you only fire once in a while, not every key repeat

def draw(self, surface):
surface.blit(self.tank, (self.x, self.y))

def update(dt):
# this dt is the same one passed when update is called and represents the number of milliseconds elapsed since last frame
if(self.firecountdown > 0) # if we are still reloading,
self.firecountdown-= dt #lower the firecountdown by time elapsed.

def fire():
if(self.firecountdown <=0) : # only fire if we are not reloading
bullet.x = tank.x
bullet.y = tank.y
bullet.speed = 10
self.firecountdown = 1000 # since we fired, we need to reload. lets reload for 1000ms



class Bullet:
def __init__(self):
self.bullet = pygame.image.load("tank_bullet.png")
self.y = tank.y
self.x = tank.x
self.speed = 0
self.forwardx = 1
self.forwardy = 0
def draw(self, surface):
surface.blit(self.x, self.y)
def update(dt):
self.x += self.forwardx * self.speed/1000 # new x coord = old + forwardx * speed * time
self.y += self.forwardy * self.speed/1000 # new y coord is similar


I am also starting to think there could be a problem in the main loop:

pygame.init()
background_color = (0,0,0)
(width, height) = (640, 480)
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('Game Window')
clock = pygame.time.Clock()
tank = Tank()
lvl_1 = Lvl_1()
bullet = Bullet()
enemy_tank = Enemy_tank()
pygame.key.set_repeat(1,50)
running = True

while running:

# this will store the time since last frame in the variable dt for use.
dt =clock.tick(30)

# Update the game state, including all entities/bullets/etc
tank.update(dt)
enemy_tank.update(dt)
bullet.update(dt)

# the game state is up to date, so lets draw it
screen.fill (background_color)

lvl_1.draw(screen)
tank.draw(screen)
bullet.draw(screen)
enemy_tank.draw(screen)
pygame.display.flip()


for event in pygame.event.get():


if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
tank.x += 5
if event.key == pygame.K_LEFT:
tank.x -= 5
# added detection for the spacebar, making tank fire bullet
if event.key == pygame.K_SPACE:
tank.fire()



I know i'm beating a dead horse here, but what is tank.self.fire()? you really... really... really need to understand the fundamentals of programming, python, and logic better before you attempt to make a game. I've edited your source as best as i can(Still don't know python, but 20 minutes on google helped, and i really should learn it anyway, for my own use at some point) and commented as much as I deemed necessary. Rather than starting a new thread when you've got this working, keep posting to this one so as not to clutter the forums. Hopefully if someone else is stuck at the same point you are, they will learn from this as well.
colts18
colts18
Ok I have made the changes and everything is good except there is an error with the tank.update(dt), enemy_tank.update(dt), and bullet.update(dt) and I have tried some changes and I googled it but I still can't figure it out do you know how? This is the error:

Traceback (most recent call last):
File "/home/bolt/Python/game.py", line 71, in
tank.update(dt)
AttributeError: Tank instance has no attribute 'update'
Burnt_Fyr
Burnt_Fyr

Ok I have made the changes and everything is good except there is an error with the tank.update(dt), enemy_tank.update(dt), and bullet.update(dt) and I have tried some changes and I googled it but I still can't figure it out do you know how? This is the error:

Traceback (most recent call last):
File "/home/bolt/Python/game.py", line 71, in
tank.update(dt)
AttributeError: Tank instance has no attribute 'update'


hmmm... Can you repost the entire source file(copy and paste prefered) as it is now so I can have a looksee?
colts18
colts18
Here it is:

import pygame

class Tank:

def __init__(self):
self.tank = pygame.image.load("tank.png")
self.x = -200
self.y = 20
self.firecountdown=0 # this will make sure you only fire once in a while, not every key repeat

def draw(self, surface):
surface.blit(self.tank, (self.x, self.y))

def update(dt):
# this dt is the same one passed when update is called and represents the number of milliseconds elapsed since last frame
if(self.firecountdown > 0):
self.firecountdown-= dt #lower the firecountdown by time elapsed.

def fire():
if(self.firecountdown <=0) : # only fire if we are not reloading
bullet.x = tank.x
bullet.y = tank.y
bullet.speed = 10
self.firecountdown = 1000 # since we fired, we need to reload. lets reload for 1000ms



class Bullet:
def __init__(self):
self.bullet = pygame.image.load("tank_bullet.png")
self.y = tank.y
self.x = tank.x
self.speed = 0
self.forwardx = 1
self.forwardy = 0
def draw(self, surface):
surface.blit(self.x, self.y)
def update(dt):
self.x += self.forwardx * self.speed/1000 # new x coord = old + forwardx * speed * time
self.y += self.forwardy * self.speed/1000 # new y coord is similar



class Lvl_1:

def __init__(self):
self.floor = pygame.image.load("floor.png")
def draw(self, surface):
surface.blit(self.floor, (0, 0))

class Enemy_tank:

def __init__(self):
self.enemy_tank = pygame.image.load("enemy_tank.png")
def draw(self, surface):
surface.blit(self.enemy_tank, (220, -40))

pygame.init()
background_color = (0,0,0)
(width, height) = (640, 480)
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('Game Window')
clock = pygame.time.Clock()
tank = Tank()
lvl_1 = Lvl_1()
bullet = Bullet()
enemy_tank = Enemy_tank()
pygame.key.set_repeat(1,50)
running = True

tank.update(dt)
enemy_tank.update(dt)
bullet.update(dt)

# the game state is up to date, so lets draw it
screen.fill (background_color)

lvl_1.draw(screen)
tank.draw(screen)
bullet.draw(screen)
enemy_tank.draw(screen)
pygame.display.flip()


for event in pygame.event.get():


if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
tank.x += 5
if event.key == pygame.K_LEFT:
tank.x -= 5
# added detection for the spacebar, making tank fire bullet
if event.key == pygame.K_SPACE:
tank.fire()

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.