Not logged inGosu Forums
Forum back to libgosu.org Help Search Register Login
Up Topic Gosu / Gosu Exchange / Map scrolling nightmare...
- - By kyonides Date 2017-09-09 17:03
I was trying to implement a map scrolling system thanks to Gosu's translate method that semiautomatically handles tiles and events offsets making it easier for game developers to handle more ordinary stuff than usual while drawing a map. Sadly you end up watching how my player and his not so heroic team makes the map look larger and darker than expected. I'm not really used dealing with scrolling issues so I thought somebody here could tell me what I'm doing wrong here hopefully... For now solving the issue with the horizontal map scroll should suffice. I only commented those lines that somehow affect map scrolling.

This happens when I go left... (Those two lonely tiles denote the actual end of the map on the righthand side.) (Check out the first image I attached.)

And this is the disaster that takes place if I go right... (Take a look at the second screenshot if you ever dare...)

class Player
  def update
    return if @halt
    @last_x = @screen_x
    @last_y = @screen_y
    return if moved?
    if Gosu.button_down?(Gosu::KbDown)
      @dir = @other_way ? 8 : 2
      return unless pass?
      @y += 0.5#actual_speed
      @screen_y = @y * 16
      update_steps
    elsif Gosu.button_down?(Gosu::KbUp)
      @dir = @other_way ? 2 : 8
      return unless pass?
      @y -= 0.5#actual_speed
      @screen_y = @y * 16
      update_steps
    elsif Gosu.button_down?(Gosu::KbLeft)
      @dir = @other_way ? 6 : 4
      return unless pass?
      @x -= 0.5#actual_speed
      @screen_x = @x * 16
      ### Calculating the horizontal offset, Current direction: Left (4)
      @offset_x = [0, -@screen_x].min if @screen_x < Map.width * 16
      update_steps
    elsif Gosu.button_down?(Gosu::KbRight)
      @dir = @other_way ? 4 : 6
      return unless pass?
      @x += 0.5#actual_speed
      @screen_x = @x * 16
      ### Calculating the horizontal offset, Current direction: Right (6)
      @offset_x = [[-@screen_x + @last_x, -Map.width * 16].max, 0].min if @screen_x > Map.width * 16
      update_steps
    end
    if button_down?(:enter) or button_down?(:return)
      Map.events.each do |event|
        blocked = block_pass?(event)
        next unless blocked
        event.react_now if event.react
        if event.button?
          event.activate
          @halt = true
          return
        end
      end
    end
  end
end

class SceneMap
  def update_scene
    @offset_x = @player.offset_x # saving last horizontal offset
    @offset_y = [-@player.screen_y + @window_height / 1.08, 0].min
    if @map_ticks > 0
      @map_ticks -= 1
      @map_texts.clear if @map_ticks == 0
    end
    @player.update # Calling Player's Update Method and setting a new horizontal offset value
    Map.events.each {|e| e.update }
    @spriteset.update
    if press_quit?
      Game.cancel_se.play
      Game.setup(SceneKUnitsMenu)
      return
    elsif button_down?(:left_ctrl)
      Game.ok_se.play
      Game.setup(SceneKUnitsAchieve)
    elsif button_down?(:caps_lock)
      Game.ok_se.play
      Game.setup(SceneKUnitsFoes)
    elsif button_down?(:left_shift)
      Game.ok_se.play
      Game.setup(SceneKBookPages, id: 1)
    end
  end

  def draw
    @window.clip_to(0,0,@window_width,@window_height) do # Some sort of Viewport singleton command
      @window.translate(@offset_x,@offset_y) do # Scroll loop
        @spriteset.draw
      end
    end
    @map_texts.each {|text| text.draw }
  end
end
Parent - - By kyonides Date 2017-09-09 17:13
Guess what? I solved it, not thanks to you, guys -_- (Yeah, I'd say I'm a bit disappointed here... but the truth is that I ended up not needing your help at all! (Tongue sticking out XP))

I'm posting this as a second part of my "sorrowful scripting" story, you'll see it does make sense after all!

As I told some guy better known as DerVVulfman last night a solution we both had found long time ago for map scrolling would NOT work on KUnits Gosu, that truth hasn't changed, BUT some creativity was needed to solve this mystery. I had partially found a way to solve it.

# Pressing Down Arrow
@offset_y = [-@screen_y + 608 / 1.08, 0].min# Works but only if you hit the lower edge of the game window then you'd make it scroll

# Pressing Right Arrow
@offset_x = [-@screen_x + 800 / 1.042, 0].min# Works but only if you hit the right edge of the game window then you'd make it scroll

Don't ask me why they had to be those values or else the minimum command wouldn't ever work! (I tried replacing the screen coordinate plus the dimension divided by a weird value with an 8, it automatically chose 0 as result, changing it to max became a new nightmare! The map either got scrolled to the left hand side almost EXPONENTIALLY or a sign change in reverted it by pushing the map to the right hand side making it arrive in Japan!)

But there's always a catch...

What if in some case like when the offset is decreasing (by adding 8 to its actual value) or the remainder (2px?) was possible in one situation? I started trying to make it work and it didn't take me long to noticed it was perfect... almost.

# Pressing Left Arrow
@offset_x += [8, -@offset_x].min if @offset_x < 0 and @screen_x - @offset_x < Map.width * 32

Yes, every single tile is 32 pixels wide and high as well. Multiplying the corresponding dimension sounds quite logical... but not to our dear Ruby Gosu engine! It made the player vanish unless IT, not the player's sprite character, got close to the left edge of the game window. Then I thought I had to define it as Map.width * 16 so it would only do it if the offset was less than half the current map's width... It messed it up as well.

After several attempts I found out the perfect value HAD to be 23.5 and it would work on ANY map that was larger than the minimum width (25 tiles * 32 pixels). Minimum sized maps wouldn't complain at all. (Shocked)

I still had a serious issue to solve, how could I make it stop waiting for the sprite to hit the opposite edge before it scrolled right? (Or to the right hand side?) I wasn't slow at all, I quickly thought a conditional statement would take care of that important job! I was WRONG! (Ticked off) I messed up once again!

Then I had a clever idea, what if the solution I applied to it's opposite direction had a clue on what to do to fix the latter?

@offset_x -= [-8, @offset_x].min if @offset_x < 0 and @screen_x - @offset_x > Map.width * 26 # Yeah I first tried with 26 for a reason I've forgotten...

How weird! It didn't help me, the screen jumped suddenly without warning! How crappy! (Happy with a sweat)

Nope, 0 would make the jump look even more extreme, as much as to beat any X-Gamer while trying to break his bones while flying briefly in the air before dropping onto the floor hitting his legs or arms hard against it...

There was a revelation finally! (Very happy + Tongue sticking out) I had to modify it slightly so it would actually help me there solving my issue.

@offset_x = [@offset_x - 8, @offset_x-@offset_x].min if @offset_x < 0 and @screen_x - @offset_x > Map.width * 26

Got close... but not enough to what people would actually expect, something was missing... (Confused)

Gotcha! I had to include a calculation included in XP, that's all I had to do!

@offset_x = [[@offset_x - 8, @offset_x-@offset_x].min, (Map.width - 25) * 32].max if @offset_x < 0 and @screen_x - @offset_x > Map.width * 26

It should work fine now for sure, I took into account that Gosu has no such real_x coordinates so I had to multiply it by the actual tile width. Wait a minute! (Ticked off) Where did my stupid map go? It looked like it did break through to the other side, whatever that was! (Feeling sick)

After a while I had found an "imperfect solution", but it WAS a solution after all!

@offset_x = [[@offset_x - 8, @offset_x-@offset_x].min, -(Map.width - 25) * 32].max if @offset_x < 0 and @screen_x - @offset_x > Map.width * 26

Say what? (Happy with a sweat) "How's that gonna help you at all?" you may ask me now. Well, it sure did! Yeah, I DO mean it! It took it a few more pixels to react than expected but no signs of oblivion was ever seen!

But how could I make it react soon enough as if I had gone through an invisible trigger right in the middle of the game window? That was the moment when I started fiddling with the map width value by increasing or decreasing it over and over again...

@offset_x = [[@offset_x - 8, @offset_x-@offset_x].min, -(Map.width - 25) * 32].max if @offset_x < 0 and @screen_x - @offset_x > Map.width * 12.75

Yeah! I DID IT! (Winks) I still don't know what the best value was 12.75, but it sure works fine! (Laughing + Tongue sticking out)

And for a while this early morning (like till 2 o'clock) I was happy with the result until I found out something was missing there... Indifferent

No, wait a second! (Shocked) My second scripting epiphany had arrived! Laughing A missing part was not the ACTUAL issue here, but an extra part was indeed! (Happy with a sweat)

@offset_x = [@offset_x - 8, -(Map.width - 25) * 32].max if @offset_x < 0 and @screen_x - @offset_x > Map.width * 12.75

Yeah, guys, the part inbetween was completely useless at that point... (Happy with a sweat)

Hold on! I clearly stated earlier that such basic solution we scripters knew all about didn't help me there at all, didn't I? And you know what, I wasn't lying, I just ignored why it didn't and never would. My solution was completely customized and that's how it should have been from the very beginning! Shocked

But what did I do? (Sarcasm)

First of all it's not that similar, I don't call the map to get any scrolling variable, and I skipped any unnecessary calculations and I never ADD the current distance, which is easily determined in my project, it's always 8 (most of the time (Laughing + Tongue sticking out) ). Oh and I substract the extra tiles width as well!

MYSTERY SOLVED! Ruby Gosu does never add distance to move everything on screen, it substract all values in a flip! (Shocked)

Solving the issue with the map's height was no problem at all! A real piece of cake! (Very happy + Tongue sticking out)

Was it? (Confused)

Take a look at the actual scrolling code and see by yourself if that was an accurate statement or not.

class Player
  def update
   return if @halt
   @last_x = @screen_x
   @last_y = @screen_y
   return if moved?
   if Gosu.button_down?(Gosu::KbDown)
     @dir = @other_way ? 8 : 2
     return unless pass?
     @y += 0.5
     @screen_y = @y * 16
     @offset_y = [@offset_y - 8, -(Map.height - 19) * 32].max if @screen_y > Map.height * 8
     update_steps
   elsif Gosu.button_down?(Gosu::KbUp)
     @dir = @other_way ? 2 : 8
     return unless pass?
     @y -= 0.5
     @screen_y = @y * 16
     @offset_y += [8, -@offset_y].min if @offset_y < 0 and @screen_y - @offset_y < Map.height * 32
     update_steps
   elsif Gosu.button_down?(Gosu::KbLeft)
     @dir = @other_way ? 6 : 4
     return unless pass?
     @x -= 0.5
     @screen_x = @x * 16
     @offset_x += [8, -@offset_x].min if @offset_x < 0 and @screen_x - @offset_x < Map.width * 23.5
     update_steps
   elsif Gosu.button_down?(Gosu::KbRight)
     @dir = @other_way ? 4 : 6
     return unless pass?
     @x += 0.5
     @screen_x = @x * 16
     @offset_x = [@offset_x - 8, -(Map.width - 25) * 32].max if @screen_x > Map.width * 12.75
     update_steps
   end
   if button_down?(:enter) or button_down?(:return)
     Map.events.each do |event|
     blocked = block_pass?(event)
     next unless blocked
     event.react_now if event.react
     if event.button?
       event.activate
       @halt = true
       return
     end
   end
  end
end

class SceneMap
  def update_scene
    if @map_ticks > 0
      @map_ticks -= 1
      @map_texts.clear if @map_ticks == 0
    end
    @player.update
    @offset_x = @player.offset_x
    @offset_y = @player.offset_y
    Map.events.each {|e| e.update }
    @spriteset.update
    if press_quit?
      Game.cancel_se.play
      Game.setup(SceneKUnitsMenu)
      return
    elsif button_down?(:left_ctrl)
      Game.ok_se.play
      Game.setup(SceneKUnitsAchieve)
    elsif button_down?(:caps_lock)
      Game.ok_se.play
      Game.setup(SceneKUnitsFoes)
    elsif button_down?(:left_shift)
      Game.ok_se.play
      Game.setup(SceneKBookPages, id: 1)
    end
  end

  def draw
    @window.clip_to(0,0,@window_width,@window_height) do
      @window.translate(@offset_x,@offset_y) do
        @spriteset.draw
      end
    end
    @map_texts.each {|text| text.draw }
  end
end
Parent - By lol_o2 Date 2017-09-10 11:04
Well, I don't get your code at all. Why are you using some @offset_x variable inside player and then assign it inside your scene and translate map with it? Isn't it better to make a variable inside scene called @screen_x, and then set it to player position after its update? (just how it's done in Cptn Ruby example) I have a feeling that your code can be MUCH simpler, unless you had your reasons to make it that way. I've used Gosu for a long time and never seen stuff like your @screen_x > Map.width * 12.75.

btw, I'm not sure if clipping to screen size affects performance at all. I think it still does some off-screen drawing, but then clips it, so you gain nothing at all. Although maybe it's implemented in more clever way.
Up Topic Gosu / Gosu Exchange / Map scrolling nightmare...

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill