/lib/chingu/parallax.rb
Ruby | 209 lines | 89 code | 30 blank | 90 comment | 20 complexity | 70d05e156cc27c0c5e05b8d52c472cec MD5 | raw file
Possible License(s): LGPL-2.1
1#-- 2# 3# Chingu -- OpenGL accelerated 2D game framework for Ruby 4# Copyright (C) 2009 ippa / ippa@rubylicio.us 5# 6# This library is free software; you can redistribute it and/or 7# modify it under the terms of the GNU Lesser General Public 8# License as published by the Free Software Foundation; either 9# version 2.1 of the License, or (at your option) any later version. 10# 11# This library is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14# Lesser General Public License for more details. 15# 16# You should have received a copy of the GNU Lesser General Public 17# License along with this library; if not, write to the Free Software 18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19# 20#++ 21 22module Chingu 23 # 24 # Class for simple parallaxscrolling 25 # 26 # See http://en.wikipedia.org/wiki/Parallax_scrolling for information about parallaxscrolling. 27 # 28 # Basic usage: 29 # @parallax = Chingu::Parallax.create(:x => 0, :y => 0) 30 # @parallax << Chingu::ParallaxLayer.new(:image => "far_away_mountins.png", :damping => 20, :center => 0) 31 # @parallax << Chingu::ParallaxLayer.new(:image => "trees.png", :damping => 5, :center => 0) 32 # 33 class Parallax < Chingu::GameObject 34 attr_reader :layers 35 36 # 37 # Options (in hash-format): 38 # 39 # repeat_x: [true|false] repeat layer on X-axis 40 # repeat_y: [true|false] repeat layer on Y-axis 41 # 42 def initialize(options = {}) 43 super(options) 44 @repeat_x = options[:repeat_x] || true 45 @repeat_y = options[:repeat_y] || false 46 47 @layers = Array.new 48 end 49 50 # 51 # Add one layer, either an ParallaxLayer-object or a Hash of options to create one 52 # You can also add new layers with the shortcut "<<": 53 # @parallax << {:image => "landscape.png", :damping => 1} 54 # 55 def add_layer(arg) 56 @layers << (arg.is_a?(ParallaxLayer) ? arg : ParallaxLayer.new(arg.merge({:parallax => self}))) 57 end 58 alias << add_layer 59 60 61 # 62 # returns true if any part of the parallax-scroller is inside the window 63 # 64 def inside_window? 65 return true if @repeat_x || @repeat_y 66 @layers.each { |layer| return true if layer.inside_window? } 67 return false 68 end 69 70 # 71 # Returns true if all parallax-layers are outside the window 72 # 73 def outside_window? 74 not inside_window? 75 end 76 77 # 78 # Parallax#camera_x= works in inverse to Parallax#x (moving the "camera", not the image) 79 # 80 def camera_x=(x) 81 @x = -x 82 end 83 84 # 85 # Parallax#camera_y= works in inverse to Parallax#y (moving the "camera", not the image) 86 # 87 def camera_y=(y) 88 @y = -y 89 end 90 91 # 92 # Get the x-coordinate for the camera (inverse to x) 93 # 94 def camera_x 95 -@x 96 end 97 98 # 99 # Get the y-coordinate for the camera (inverse to y) 100 # 101 def camera_y 102 -@y 103 end 104 105 # 106 # TODO: make use of $window.milliseconds_since_last_update here! 107 # 108 def update 109 @layers.each do |layer| 110 layer.x = @x / layer.damping 111 layer.y = @y / layer.damping 112 113 # This is the magic that repeats the layer to the left and right 114 layer.x -= layer.image.width while (layer.repeat_x && layer.x > 0) 115 116 # This is the magic that repeats the layer to the left and right 117 layer.y -= layer.image.height while (layer.repeat_y && layer.y > 0) 118 end 119 end 120 121 # 122 # Draw 123 # 124 def draw 125 @layers.each do |layer| 126 save_x, save_y = layer.x, layer.y 127 128 # If layer lands inside our window and repeat_x is true (defaults to true), draw it until window ends 129 while layer.repeat_x && layer.x < $window.width 130 while layer.repeat_y && layer.y < $window.height 131 layer.draw 132 layer.y += layer.image.height 133 end 134 layer.y = save_y 135 136 layer.draw 137 layer.x += layer.image.width 138 end 139 140 # Special loop for when repeat_y is true but not repeat_x 141 if layer.repeat_y && !layer.repeat_x 142 while layer.repeat_y && layer.y < $window.height 143 layer.draw 144 layer.y += layer.image.height 145 end 146 end 147 148 layer.x = save_x 149 end 150 self 151 end 152 end 153 154 # 155 # ParallaxLayer is mainly used by class Parallax to keep track of the different layers. 156 # If you @parallax << { :image => "foo.png" } a ParallaxLayer will be created automaticly from that Hash. 157 # 158 # If no zorder is provided the ParallaxLayer-class increments an internal zorder number which will 159 # put the last layer added on top of the rest. 160 # 161 class ParallaxLayer < Chingu::GameObject 162 attr_reader :damping 163 attr_accessor :repeat_x, :repeat_y 164 165 def initialize(options) 166 @parallax = options[:parallax] 167 # No auto update/draw, the parentclass Parallax takes care of that! 168 options.merge!(:visible => false, :paused => true) 169 170 options = {:rotation_center => @parallax.options[:rotation_center]}.merge(options) if @parallax 171 172 # 173 # Default arguments for repeat_x and repeat_y 174 # If no zorder is given, use a global incrementing counter. 175 # First added, furthest behind when drawn. 176 # 177 options = { 178 :repeat_x => true, 179 :repeat_y => false, 180 :zorder => @parallax ? (@parallax.zorder + @parallax.layers.count) : 100 181 }.merge(options) 182 183 @repeat_x = options[:repeat_x] 184 @repeat_y = options[:repeat_y] 185 186 super(options) 187 188 @damping = options[:damping] || 1 189 end 190 191 # 192 # Gets pixel from layers image 193 # The pixel is from the window point of view, so coordinates are converted: 194 # 195 # @parallax.layers.first.get_pixel(10, 10) # the visible pixel at 10, 10 196 # @parallax.layers.first.image.get_pixel(10, 10) # gets pixel 10, 10 from layers image no matter where layer is positioned 197 # 198 def get_pixel(x, y) 199 image_x = x - @x 200 image_y = y - @y 201 202 # On a 100 x 100 image, get_pixel works to 99 x 99 203 image_x -= @image.width while image_x >= @image.width 204 205 @image.get_pixel(image_x, image_y) 206 end 207 208 end 209end