Not logged inGosu Forums
Forum back to libgosu.org Help Search Register Login
Up Topic Gosu / Gosu Exchange / OpenGL vs fake 3D
- - By rremedio Date 2015-11-20 15:50
So I've been playing with ideas for a side-scrolling adventure game (something between a run 'n gun, a beat 'em up and Flashback: The Quest for Identity).

One thing that I'd like to add is that "3d" effect when the ground scrolls, like some 16bit side-scrolling games (Mega Drive's The Adventures of Batman and Robin comes to mind), just because it is neat. After an hour remembering my old algebra and trigonometry lessons (buried deep in my mind after 20 years of using computers and calculators) it worked very well with tiles and the draw_as_quad method. Then I did the same for some basic 3d objects, like walls and boxes, just to see how hard and demanding it would be and it also worked nicely. But I wonder if I'm to include some basic 3d objects in my 2d games, should I forget about that and go straight OpenGL? I tried to make OpenGL work a few years ago and I gave up soon because of installation problems, but I'm sure it was something wrong I was doing.

It is not that I want a full 3d scenery but if I start to add too many objects and the game is supposed to have a few platforms, will I regret doing it in plain Gosu?

Thanks in advance! :D
Parent - By bestguigui Date 2015-11-21 09:34
When faking 3D is hard, you could of course consider using proper OpenGL. And you could maybe use the great Gosu::Window#draw.gl(z) method to generate a "sprite" with a 3D model and to do a composition mixing your 2D/3D elements, playing with the Z value.

https://www.libgosu.org/rdoc/Gosu.html#gl-class_method

Using glViewport and a proper camera setting, I'm sure it could render just great !
Parent - - By jlnr (dev) Date 2015-11-21 09:47
I've used fake 3D boxes in a few games and was quite happy with the results. Usually, what I've done is just to calculate the x/y delta of each vertex to the screen centre, then multiplied it by 1.1 or some other value > 1.0. Then I use draw_quad to connect the original vertices and the offset vertices, drawing up to five images per box.
So if part of your problem is that your math has become tricky, you can maybe simplify it like that :)

Very simple fake 3D in action -> http://ludumdare.com/compo/2009/04/19/starferret-48h-final/
Parent - - By rremedio Date 2015-11-21 14:03
Hummm...maybe I'm overcomplicating things by using 3 images for each box. I'm gonna try this trick.
Parent - By jlnr (dev) Date 2015-11-21 21:33
Oh, you'll still need to draw things up to 5 times, depending on the camera position. I just wanted to say that the math for this is simple, since you've mentioned trigonometry - but I see below that you're already using a very simple formula :)
- - By rremedio Date 2015-11-21 13:58
I think I should explain myself better

Here is more or less what I want to do (the slowdown is because of the recording software). In the game that I have in mind, there would be no auto-scrolling.

https://www.youtube.com/watch?v=n9yoDqZBKgQ

The math is not tricky. It is just one equation that takes the y position of the horizon on the screen and returns the x position of a point. Since the camera will never need to zoom in/out, I think it is good enough for the scrolling ground and in this example it works ok for the boxes.

But for each draw_as_quad it will be called 2-4 times/frame and I wonder if this isn't exactly what I should be using OpenGL for, especially with the boxes (which display 3-4 tiles/box every frame).
Parent - - By bestguigui Date 2015-11-21 16:00
I just love it ! Could you share the code for this ? I'm curious :)
Parent - By rremedio Date 2015-11-21 17:16
Thanks!

Well, this code is a mess. I wouldn't use any of this in any project. It is just to test the validity of the equation.

require 'gosu'

White=Gosu::Color::WHITE

class GameWindow < Gosu::Window
 
  def initialize
  super(600, 400, true)
    $window=self
    @img1=Gosu::Image.new(self, "tile1.png", true) #gray tile 40x40
    @img3=Gosu::Image.new(self, "tile3.png", true) #yellow tile 80X80
   
    @wall=[]
    @f1=[]
   
    for i in 0..18
      #[tile img, x position]
      @wall<<[@img1,i*40] #wall tiles
      @f1<<[@img3,i*40] #floor tiles
    end
   
    @cx=350 #sort of camera_x
    @cy=180.1 #horizon_y
    @box=[]
    for i in 0..2
      @box<<Box.new(10+i*200, @cx, @cy)
    end
    @ms=2
  end
 
  def update
    self.caption = "Editor: #{Gosu::fps} fps"
      @wall.each do |pie|
        pie[1]-=@ms
      end
      @f1.each do |pie|
        pie[1]-=@ms
      end
      @box.each do |box|
        box.move(-@ms)
      end
    #end
   
    @box.each do |box|
      box.update
    end
  end
 
  #this could be much simpler, probably
  #I just made a drawing and used basic math
  #x1,y1 are coordinates of a point (1) in the screen
  #y2 is the y coordinate on the screen of another point (2)
  #points 1 and 2 are conected by a line, perpendicular to the wall
  def getp(x1,y1,y2)
    a=(x1*(@cy-y2)+@cx*(y2-y1))/(@cy-y1)
    a
  end
   
  def draw
    @wall.each do |brick|
      if brick[1].between?(-40,740) #if we want to draw it
       
        #fill the screen vertically
        brick[0].draw(brick[1], 0, 0, 1, 1, White, :default)
        brick[0].draw(brick[1], 40, 0, 1, 1, White, :default)
        brick[0].draw(brick[1], 80, 0, 1, 1, White, :default)
        brick[0].draw(brick[1], 120, 0, 1, 1, White, :default)
        brick[0].draw(brick[1], 160, 0, 1, 1, White, :default)
        brick[0].draw(brick[1], 200, 0, 1, 1, White, :default)
      else
      #when scrolling, we move the tile to right end if it moves left off the screen
        if brick[1]<-40
          brick[1]=@wall.max_by{|a|a[1]}[1]+40
        end
      end
    end
   
    #we should have a class for the floor tiles and we should calculate the coordinates in the update step, but I was just checking if the calculations are correct
    @f1.each do |b|
      if b[1].between?(-40,740)
        #drawing the floor tiles
        #the floor "top"
        b[0].draw_as_quad(b[1],240,White,b[1]+40,240,White,getp(b[1],240,300),300,White,getp(b[1]+40,240,300),300,White,1,:default)
        #the "front" face of the floor "blocks"
        b[0].draw_as_quad(getp(b[1],240,300),300,White,getp(b[1]+40,240,300),300,White,getp(b[1],240,300),350,White,getp(b[1]+40,240,300),350,White,1,:default)
      else
        #when scrolling, we move the tile to right end if it moves left off the screen
        if b[1]<-40
          b[1]=@f1.max_by{|a|a[1]}[1]+40
        end
      end
    end
    @box.each do |box|
      box.draw
    end
  end
   
  def button_down(id)
    if id == Gosu::KbEscape
      puts @cy
      $window.close
    end
   
    #move the horizon line up/down
    if id==Gosu::KbUp
      @cy-=5
      @box.each do |box|
        box.move_horizon(-5)
      end
    end
    if id==Gosu::KbDown
      if @cy<235
        @cy+=5
        @box.each do |box|
          box.move_horizon(5)
        end
      end
    end
   
   
  end
 
end

class Box
  def initialize (x, cx, cy)
    @cx=cx
    @cy=cy
    @x=x
    @img2=Gosu::Image.new($window, "tile2.png", true) #red tile 40x40
  end
 
  def getp(x1,y1,y2)
    a=(x1*(@cy-y2)+@cx*(y2-y1))/(@cy-y1)
    a
  end
 
  def move(x)
    @x+=x
  end
 
  def move_horizon(y)
    @cy+=y
  end
 
  def update
    #when scrolling, we move the block to right end if it moves left off the screen
    if @x<-40
      @x=740
    end
  end
 
 
  def draw
    if @x.between?(-42,702)
      #drawing the faces of the boxes
      #we assume the block top being below the horizon
      #we can easily make this more generic by adding the calcs for it being above the horizon
      #again, we should add variables for the box shape and make calculations in the update step
     
      #front face
      @img2.draw_as_quad(getp(@x,240,250),230,White,getp(@x+40,240,250),230,White,getp(@x,240,250),250,White,getp(@x+40,240,250),250,White,1,:default)
      #top face
      @img2.draw_as_quad(@x,225,White,@x+40,225,White,getp(@x,240,250),230,White,getp(@x+40,240,250),230,White,1,:default)
      #left face, only displayed if the block left face is to the right of @cx
      if @x>@cx
        @img2.draw_as_quad(@x,225,White,getp(@x,240,250),230,White,@x,240,White,getp(@x,240,250),250,White,1,:default)
      end
      #right face, only displayed if the block right face is to the left of @cx
      if (@x+40)<@cx
        @img2.draw_as_quad(getp(@x+40,240,250),230,White,@x+40,225,White,getp(@x+40,240,250),250,White,@x+40,240,White,1,:default)
      end
    end
   
  end
end

window = GameWindow.new
window.show
Up Topic Gosu / Gosu Exchange / OpenGL vs fake 3D

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill