/lib/chingu/parallax.rb
http://github.com/ippa/chingu · Ruby · 209 lines · 89 code · 30 blank · 90 comment · 20 complexity · 70d05e156cc27c0c5e05b8d52c472cec MD5 · raw file
- #--
- #
- # Chingu -- OpenGL accelerated 2D game framework for Ruby
- # Copyright (C) 2009 ippa / ippa@rubylicio.us
- #
- # This library is free software; you can redistribute it and/or
- # modify it under the terms of the GNU Lesser General Public
- # License as published by the Free Software Foundation; either
- # version 2.1 of the License, or (at your option) any later version.
- #
- # This library is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- # Lesser General Public License for more details.
- #
- # You should have received a copy of the GNU Lesser General Public
- # License along with this library; if not, write to the Free Software
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- #
- #++
- module Chingu
- #
- # Class for simple parallaxscrolling
- #
- # See http://en.wikipedia.org/wiki/Parallax_scrolling for information about parallaxscrolling.
- #
- # Basic usage:
- # @parallax = Chingu::Parallax.create(:x => 0, :y => 0)
- # @parallax << Chingu::ParallaxLayer.new(:image => "far_away_mountins.png", :damping => 20, :center => 0)
- # @parallax << Chingu::ParallaxLayer.new(:image => "trees.png", :damping => 5, :center => 0)
- #
- class Parallax < Chingu::GameObject
- attr_reader :layers
- #
- # Options (in hash-format):
- #
- # repeat_x: [true|false] repeat layer on X-axis
- # repeat_y: [true|false] repeat layer on Y-axis
- #
- def initialize(options = {})
- super(options)
- @repeat_x = options[:repeat_x] || true
- @repeat_y = options[:repeat_y] || false
-
- @layers = Array.new
- end
-
- #
- # Add one layer, either an ParallaxLayer-object or a Hash of options to create one
- # You can also add new layers with the shortcut "<<":
- # @parallax << {:image => "landscape.png", :damping => 1}
- #
- def add_layer(arg)
- @layers << (arg.is_a?(ParallaxLayer) ? arg : ParallaxLayer.new(arg.merge({:parallax => self})))
- end
- alias << add_layer
-
- #
- # returns true if any part of the parallax-scroller is inside the window
- #
- def inside_window?
- return true if @repeat_x || @repeat_y
- @layers.each { |layer| return true if layer.inside_window? }
- return false
- end
- #
- # Returns true if all parallax-layers are outside the window
- #
- def outside_window?
- not inside_window?
- end
-
- #
- # Parallax#camera_x= works in inverse to Parallax#x (moving the "camera", not the image)
- #
- def camera_x=(x)
- @x = -x
- end
- #
- # Parallax#camera_y= works in inverse to Parallax#y (moving the "camera", not the image)
- #
- def camera_y=(y)
- @y = -y
- end
- #
- # Get the x-coordinate for the camera (inverse to x)
- #
- def camera_x
- -@x
- end
- #
- # Get the y-coordinate for the camera (inverse to y)
- #
- def camera_y
- -@y
- end
-
- #
- # TODO: make use of $window.milliseconds_since_last_update here!
- #
- def update
- @layers.each do |layer|
- layer.x = @x / layer.damping
- layer.y = @y / layer.damping
-
- # This is the magic that repeats the layer to the left and right
- layer.x -= layer.image.width while (layer.repeat_x && layer.x > 0)
-
- # This is the magic that repeats the layer to the left and right
- layer.y -= layer.image.height while (layer.repeat_y && layer.y > 0)
- end
- end
-
- #
- # Draw
- #
- def draw
- @layers.each do |layer|
- save_x, save_y = layer.x, layer.y
-
- # If layer lands inside our window and repeat_x is true (defaults to true), draw it until window ends
- while layer.repeat_x && layer.x < $window.width
- while layer.repeat_y && layer.y < $window.height
- layer.draw
- layer.y += layer.image.height
- end
- layer.y = save_y
-
- layer.draw
- layer.x += layer.image.width
- end
-
- # Special loop for when repeat_y is true but not repeat_x
- if layer.repeat_y && !layer.repeat_x
- while layer.repeat_y && layer.y < $window.height
- layer.draw
- layer.y += layer.image.height
- end
- end
- layer.x = save_x
- end
- self
- end
- end
-
- #
- # ParallaxLayer is mainly used by class Parallax to keep track of the different layers.
- # If you @parallax << { :image => "foo.png" } a ParallaxLayer will be created automaticly from that Hash.
- #
- # If no zorder is provided the ParallaxLayer-class increments an internal zorder number which will
- # put the last layer added on top of the rest.
- #
- class ParallaxLayer < Chingu::GameObject
- attr_reader :damping
- attr_accessor :repeat_x, :repeat_y
-
- def initialize(options)
- @parallax = options[:parallax]
- # No auto update/draw, the parentclass Parallax takes care of that!
- options.merge!(:visible => false, :paused => true)
-
- options = {:rotation_center => @parallax.options[:rotation_center]}.merge(options) if @parallax
-
- #
- # Default arguments for repeat_x and repeat_y
- # If no zorder is given, use a global incrementing counter.
- # First added, furthest behind when drawn.
- #
- options = {
- :repeat_x => true,
- :repeat_y => false,
- :zorder => @parallax ? (@parallax.zorder + @parallax.layers.count) : 100
- }.merge(options)
-
- @repeat_x = options[:repeat_x]
- @repeat_y = options[:repeat_y]
-
- super(options)
-
- @damping = options[:damping] || 1
- end
-
- #
- # Gets pixel from layers image
- # The pixel is from the window point of view, so coordinates are converted:
- #
- # @parallax.layers.first.get_pixel(10, 10) # the visible pixel at 10, 10
- # @parallax.layers.first.image.get_pixel(10, 10) # gets pixel 10, 10 from layers image no matter where layer is positioned
- #
- def get_pixel(x, y)
- image_x = x - @x
- image_y = y - @y
-
- # On a 100 x 100 image, get_pixel works to 99 x 99
- image_x -= @image.width while image_x >= @image.width
-
- @image.get_pixel(image_x, image_y)
- end
-
- end
- end