Not logged inGosu Forums
Forum back to libgosu.org Help Search Register Login
Up Topic Gosu / Gosu Exchange / Enemy should trigger a bullet
- - By zero Date 2015-02-22 20:06
Hi Everyone,

I have currently some difficulties to implement enemy action / shooting, if an enemy is "close" to the player object. My problem is how do I trigger a shot from an array of bullets?

I have two arrays: An enemy array and a bullet array. Each Enemy gets a number of bullets. The enemy should trigger one bullet (not the whole array) if Gosu::distance(enemy.x, 0, @player.x, 0) < 15 is true.

Thx for having a look at my code. I appreciate every message. Please ask, if something in the code looks confusing. I still learn to code, unfortunatley it seems that I do not have any talent for it.

Since the forum does not have any code highlighting, I've used cloud app to share the code:

http://cl.ly/code/2M2w2K2u2h0j
Parent - - By lol_o2 Date 2015-02-22 20:48 Edited 2015-02-22 20:54
When enemy is close to player, it will shoot ONE bullet. But problem is that he does it every game tick. You have to remember that each line of the code is executed 60 time per seconds, which means when you are close to enemy, he shoots 60 bullets per second.

To fix your code, you have to add some delay between shots. The best way (and probably only one) is to use some variable.

The code:

class Enemy
  def initialize
    ...
    @delay=0 #initialize the variable
  end

  def update
    ...
    @delay-=1 #decrease the variable so delay isn't infinite
  end
 
  def shoot
    return if @delay > 0 #don't shoot if delay is not finished
    bullet = @bullets.find { |bullet| not bullet.shot }
    if bullet
      @delay = 60 #delay shot for 60 game ticks (one second)
      bullet.shot = true
      bullet.sample.play
    end
  end
...
end


Tip:
Gosu::distance is for calculating distance between two points. When comparing only one coordinate, it may be more efficient to use (enemy.x - @player.x).abs instead.
Parent - - By zero Date 2015-02-23 18:18
Thank you for the help. I have indeed ignored the code execution 60 times a second. However, now it seems that the bullet gets triggered (I hear the sample play) but there is rarely a bullet on the screen. I have not discovered the pattern behind it yet. It's odd. Your code already helped me.
Parent - - By lol_o2 Date 2015-02-23 20:47
You set bullet.shot=true, which only makes bullet updated and drawn. But to make your enemy actually shoot the bullet, you need to set it's initial position (probably at enemy's "shootpoint"). As you made currently, the bullet appears "somewhere" and you can't see it.
Parent - - By zero Date 2015-02-23 21:02
Should not this (in the bullet class - see linked file in the initial post)

  @x = @entity.x
  @y = @entity.y

takes care of that?
Parent - - By lol_o2 Date 2015-02-24 07:32
I misread your post a bit, you said the bullet appears, only rarely. This @x=... should work, but I see another problem: Array.new(5) { Bullet.new(enemy, window) } It creates array filled with the same bullet. So your enemy shoots the same bullet every time and he won't shoot when this bullet is still of the screen. You have to make array of five instances of bullets.
Parent - - By zero Date 2015-02-25 13:12
I think when I create the array like this:

@bullets = Array.new(5) { Bullet.new(self, window) }

I do not have 5 identical objects, this would be the case if I create the array like this

@bullets = Array.new(5, Bullet.new(self, window)

or did I misinterpret your answer? So the @bullets array should hold 5 different bullet objects to my understanding.
Parent - - By lol_o2 Date 2015-02-25 16:28
Yes, it should hold different bullets, and the way you did now makes one bullet. One instance of Bullet object can't be shot multiple times.

There are two (or more) approaches to make array filled with unique objects:

First:
@bullets = [ Bullet.new(self, window), Bullet.new(self, window), Bullet.new(self, window), Bullet.new(self, window), Bullet.new(self, window) ]

Second (more suitable for bigger arrays):
@bullets = []
5.times{ @bullets << Bullet.new(self, window) }
Parent - - By RunnerPack Date 2015-02-25 18:40
Actually, passing a block to Array.new does create separate objects. I think it's functionally identical to your second example.

This can be readily tested in IRB:

irb(main):005:0> bullets = Array.new(5) { Numeric.new }
=> [#<Numeric:0x2cdaf38>, #<Numeric:0x2cdaf20>, #<Numeric:0x2cdaf08>, #<Numeric:0x2cdaef0>, #<Numeric:0x2cdaed8>]


As you can see, five objects with different IDs are created.
Parent - - By lol_o2 Date 2015-02-25 18:59
Yeah, I was referring to Ruby documentation, but I misread it. Sorry.
Parent - By RunnerPack Date 2015-02-26 04:36
That's okay... it happens to the best of us :D
Parent - By zero Date 2015-02-27 08:38
Yep, also no problem on this site. You already helped me a lot with the loop execution and the count variable.
Parent - - By zero Date 2015-02-27 21:09
Ok, there is one enemy-object out of the enemies array that shoots a bullet. The sample is played and the bullet is being displayed correctly. The other enemy-objects are only triggering the sample to play. No bullet is drawn.
Parent - - By lol_o2 Date 2015-02-28 20:44
@enemies.each { |enemy|
      @enemy_bullets = Array.new(5) { Bullet.new(enemy, window) }
      enemy.load_bullets(@enemy_bullets)
    }

This ^

Each time you are replacing @enemy_bullets with new array, so only the last enemy has it's bullets drawn.
Parent - By zero Date 2015-03-02 07:21
Thank you for the explanation! I already assumed it has to be this part, but I thought, this is the way to initialize and load for each enemy his bullets. I try to find out, how I solve this.
Parent - By zero Date 2015-03-02 15:04
SOLVED! :) Thanks for the clear explanation. I had to initiate the bullets for the enemy object.
Up Topic Gosu / Gosu Exchange / Enemy should trigger a bullet

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill