diff --git a/README.rdoc b/README.rdoc index 7c1b7fc6..e53b5b26 100644 --- a/README.rdoc +++ b/README.rdoc @@ -89,9 +89,10 @@ Both $window and game states gets some new graphical helpers, currently only 3, fill_gradient() # Fills window or a given rect with a gradient between two colors. If you base your models on GameObject (or BasicGameObject) you get: - Enemy.all # Returns all object based on this class - Enemy.each # Iterates through all objects of class Enemt. - Enemy.destroy_if(&block) # destroy all objects for which &block returns true + Enemy.all # Returns an Array of all Enemy-instances + Enemy.size # Returns the amount of Enemy-instances + Enemy.destroy_all # Destroys all Enemy-instances + Enemy.destroy_if(&block) # Destroy all objects for which &block returns true == BASICS / EXAMPLES @@ -417,6 +418,65 @@ Or Chingus shortcut: Chingus inputhandler will detect that Menu is a gamestate-class, create a new instance, cache it and activate it with push_game_state(). +=== Traits +Traits (often called behaivors) is a way of adding logic to any class inheriting from BasicGameObject / GameObject. +Chingus trait-implementation is just ordinary ruby modules with 3 special methods: + - setup_trait + - update_trait + - draw_trait +Each of those 3 methods must call "super" to continue the trait-chain. + +The flow for a game object then becomes: + +-- creating the object +1) GameObject#initialize(options) +2) GameObject#setup_trait(options) +-- each game iteration +3) GameObject#draw_trait +4) GameObject#draw +5) GameObject#update_trait +6) GameObject#update + + +There's a couple of traits included as default in Chingu: + +==== Trait "timer" +Adds timer functionallity to your game object + during(300) { @color = Color.new(0xFFFFFFFF) } # forces @color to white ever update for 300 ms + after(400) { self.destroy } # destroy object after 400 ms + between(1000,2000) { self.rotate(10) } # starting after 1 second, call rotate(10) every update during 1 second + +==== Trait "velocity" +Adds variables velocity_x, velocity_y, acceleration_x, acceleration_y, max_velocity to game object. +They modify x, y as you would expect. *speed / angle will come* + + +== (IN DEVELOPMENT) Trait "effect" +Adds ability to automaticly fade, rotate and scale game objects. +* API isn't stabilized yet! * + +== (IN DEVELOPMENT) Trait "collision_detection" +Adds class and instance methods for basic collision detection. + + # Class method example + # This will collide all Enemy-instances with all Bullet-instances using the attribute #radius from each object. + Enemy.each_radius_collision(Bullet) do |enemy, bullet| + end + + # You can also use the instance methods. This will use the Rect bounding_box from @player and each EnemyRocket-object. + @player.each_bounding_box_collision(EnemyRocket) do |player, enemyrocket| + player.die! + end + +* API isn't stabilized yet! * + +== (IN DEVELOPMENT) Trait "retrofy" +Providing easier handling of the "retrofy" effect (non-blurry zoom) +Aims to help out when using zoom-factor to create a retrofeeling with big pixels. +Provides screen_x and screen_y which takes the zoom into account +Also provides new code for draw() which uses screen_x / screen_y instead of x / y + + === Assets / Paths You might wonder why this is necessary in the straight Gosu example: diff --git a/examples/example11.rb b/examples/example11.rb index 6f018017..c2de5e64 100644 --- a/examples/example11.rb +++ b/examples/example11.rb @@ -1,51 +1,51 @@ require 'rubygems' -require 'opengl' -require 'gosu' -#include Gosu +require File.join(File.dirname($0), "..", "lib", "chingu") +include Gosu -class Game < Gosu::Window +# +# Parallax-example +# Images from http://en.wikipedia.org/wiki/Parallax_scrolling +# +class Game < Chingu::Window def initialize - super(400,400,false) + super + self.input = { :holding_left => :scroll_left, + :holding_right => :scroll_right, + :holding_up => :scroll_up, + :holding_down => :scroll_down, + :escape => :exit } + + self.caption = "Chingu::Parallax example. Scroll with left/right arrows." + + @parallax = Chingu::Parallax.create(:x => 0, :y => 0, :center_x => 0, :center_y => 0) + + # + # If no :zorder is given to @parallax.add_background it defaults to first added -> lowest zorder + # Everywhere the :image argument is used, theese 2 values are the Same: + # 1) Image["foo.png"] 2) "foo.png" + # + # TODO: scrolling to left borks outm, fix. + get rid of center_x / center_y args in a clean way. + @parallax << {:image => "paralaxx2", :damping => 100, :center => 0) + @parallax << {:image => "parallax-scroll-example-layer-1.png", :damping => 10, :center => 0) + @parallax << {:image => "paralaxx2.png", :damping => 5, :center => 0) end - def update + def scroll_left + @parallax.x -= 2 end - def draw - gl do - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - - # Reset the view - glLoadIdentity - - # Move to the left 1.5 units and into the screen 6.0 units - glTranslate(-1.5, 0.0, -6.0) - - # -- Draw a triangle -- - glColor(1.0,1.0,1.0) - - # Begin drawing a polygon - glBegin(GL_POLYGON) - glVertex3f( 0.0, 1.0, 0.0) # Top vertex - glVertex3f( 1.0, -1.0, 0.0) # Bottom right vertex - glVertex3f(-1.0, -1.0, 0.0) # Bottom left vertex - # Done with the polygon - glEnd - - # Move 3 units to the right - glTranslate(3.0, 0.0, 0.0) - - # -- Draw a square (quadrilateral) -- - # Begin drawing a polygon (4 sided) - glBegin(GL_QUADS) - glVertex3f(-1.0, 1.0, 0.0) # Top Left vertex - glVertex3f( 1.0, 1.0, 0.0) # Top Right vertex - glVertex3f( 1.0, -1.0, 0.0) # Bottom Right vertex - glVertex3f(-1.0, -1.0, 0.0) # Bottom Left - glEnd - glFlush - end + def scroll_right + @parallax.x += 2 end + + def scroll_up + @parallax.y -= 2 + end + + def scroll_down + @parallax.y += 2 + end + end -Game.new.show +Game.new.show \ No newline at end of file diff --git a/examples/example3.rb b/examples/example3.rb index 98736058..724e4cd5 100644 --- a/examples/example3.rb +++ b/examples/example3.rb @@ -9,7 +9,9 @@ class Game < Chingu::Window def initialize super - self.input = {:holding_left => :scroll_left, :holding_right => :scroll_right, :escape => :exit} + self.input = { :holding_left => :scroll_left, :holding_right => :scroll_right, :escape => :exit } + + self.caption = "Chingu::Parallax example. Scroll with left/right arrows." @parallax = Chingu::Parallax.create(:x => 0, :y => 0, :center_x => 0, :center_y => 0) @@ -19,10 +21,10 @@ def initialize # 1) Image["foo.png"] 2) "foo.png" # # TODO: scrolling to left borks outm, fix. + get rid of center_x / center_y args in a clean way. - @parallax.add_background(:image => "Parallax-scroll-example-layer-0.png", :damping => 100, :center_x => 0, :center_y => 0) - @parallax.add_background(:image => "Parallax-scroll-example-layer-1.png", :damping => 10, :center_x => 0, :center_y => 0) - @parallax.add_background(:image => "Parallax-scroll-example-layer-2.png", :damping => 5, :center_x => 0, :center_y => 0) - @parallax.add_background(:image => "Parallax-scroll-example-layer-3.png", :damping => 1, :center_x => 0, :center_y => 0) + @parallax.add_background(:image => "Parallax-scroll-example-layer-0.png", :damping => 100, :center => 0) + @parallax.add_background(:image => "Parallax-scroll-example-layer-1.png", :damping => 10, :center => 0) + @parallax.add_background(:image => "Parallax-scroll-example-layer-2.png", :damping => 5, :center => 0) + @parallax << {:image => "Parallax-scroll-example-layer-3.png", :damping => 1, :center => 0} # you can also add like this end def scroll_left @@ -32,6 +34,7 @@ def scroll_left def scroll_right @parallax.x += 2 end + end Game.new.show \ No newline at end of file diff --git a/examples/example9.rb b/examples/example9.rb index 56379543..a678dba5 100644 --- a/examples/example9.rb +++ b/examples/example9.rb @@ -38,7 +38,7 @@ def initialize(options) @velocity_y = options[:velocity_y] || 1 + rand(2) @bounding_box = Rect.new([@x, @y, 10, 10]) - @radius = 12 + @radius = 6 @blue = Color.new(255,100,255,255) @red = Color.new(255,255,10,10) @@ -98,7 +98,7 @@ def update end def draw - $window.caption = "Radius based collision Detection between all particles. Particles#: #{game_objects.size} - FPS: #{$window.fps}" + $window.caption = "radius based iterative collision detection. Particles#: #{game_objects.size}, Collisionchecks each gameloop: ~#{game_objects.size**2} - FPS: #{$window.fps}" super end end diff --git a/lib/chingu/actor.rb b/lib/chingu/actor.rb index 0b52f993..ad2d2c9c 100644 --- a/lib/chingu/actor.rb +++ b/lib/chingu/actor.rb @@ -2,6 +2,8 @@ module Chingu # # A game object class with most components included, nice for quick prototypes # + # TODO: we probably wanna expand this concept or remove Actor as a whole. + # class Actor < Chingu::GameObject has_traits :effect, :velocity, :input diff --git a/lib/chingu/basic_game_object.rb b/lib/chingu/basic_game_object.rb index a9e3ac91..6b11cb08 100644 --- a/lib/chingu/basic_game_object.rb +++ b/lib/chingu/basic_game_object.rb @@ -107,7 +107,7 @@ def self.size # def self.destroy_if(&block) all.each do |object| - object.destroy! if yield(object) + object.destroy if yield(object) end end @@ -123,9 +123,10 @@ def self.destroy_all # Removes object from the update cycle and freezes the object to prevent further modifications. # If the object isn't being managed by Chingu (ie. you're doing manual update/draw calls) the object is only frozen, not removed from any updae cycle (because you are controlling that). # - def destroy! + def destroy @parent.remove_game_object(self) if @parent self.freeze - end + end + alias :destroy! :destroy end end \ No newline at end of file diff --git a/lib/chingu/game_object.rb b/lib/chingu/game_object.rb index 54b4378d..7dfe4a4d 100644 --- a/lib/chingu/game_object.rb +++ b/lib/chingu/game_object.rb @@ -45,26 +45,18 @@ def initialize(options = {}) @y = options[:y] || 0 @angle = options[:angle] || 0 - @center_x = options[:center_x] || options[:center] || 0.5 - @center_y = options[:center_y] || options[:center] || 0.5 - @factor_x = options[:factor_x] || options[:factor] || 1.0 - @factor_y = options[:factor_y] || options[:factor] || 1.0 - - # faster? - #self.center = options[:center] || 0.5 - #self.factor = options[:factor] || 1.0 - #@center_x = options[:center_x] || 0.5 - #@center_y = options[:center_y] || 0.5 - #@factor_x = options[:factor_x] || 1.0 - #@factor_y = options[:factor_y] || 1.0 + self.center = options[:center] || 0.5 + self.factor = options[:factor] || 1.0 + @center_x = options[:center_x] if options[:center_x] + @center_y = options[:center_y] if options[:center_y] + @factor_x = options[:factor_x] if options[:factor_x] + @factor_y = options[:factor_y] if options[:factor_y] if options[:color].is_a?(Gosu::Color) @color = options[:color] - elsif options[:color].is_a? Bignum - @color = Gosu::Color.new(options[:color]) else - @color = Gosu::Color.new(0xFFFFFFFF) - end + @color = Gosu::Color.new(options[:color] || 0xFFFFFFFF) + end @mode = options[:mode] || :default # :additive is also available. @zorder = options[:zorder] || 100 diff --git a/lib/chingu/parallax.rb b/lib/chingu/parallax.rb index 80ca695c..1beb672e 100644 --- a/lib/chingu/parallax.rb +++ b/lib/chingu/parallax.rb @@ -6,6 +6,8 @@ module Chingu class Parallax < Chingu::GameObject attr_reader :backgrounds + # + # Options (in hash-format): # # repeat: [true|false] When one background ends within the screen, repeat/loop it # @@ -17,17 +19,13 @@ def initialize(options) # # Add one background, either an ParallaxBackground-object or a Hash of options to create one + # You can also add new backgrounds with the shortcut "<<": + # @parallax << {:image => "landscape.png", :damping => 1} # def add_background(arg) @backgrounds << (arg.is_a?(ParallaxBackground) ? arg : ParallaxBackground.new(arg)) end - - # - # Shortcut for #add_background - # - def <<(arg) - self.add_background(arg) - end + alias << add_background # # TODO: make use of $window.milliseconds_since_last_update here! @@ -36,6 +34,9 @@ def update @backgrounds.each do |background| background.x = -@x / background.damping background.y = @y / background.damping + + # This is the magic that repeats the background to the left and right + background.x -= background.image.width while background.x > 0 end end @@ -44,17 +45,16 @@ def update # def draw @backgrounds.each do |background| - #background.image.draw(real_x, real_y, background.zorder) background.draw save_x = background.x + ## If background lands inside our screen, repeat it while (background.x + background.image.width) < $window.width - background.x += background.image.width + background.x += background.image.width background.draw - #background.x -= background.image.width - #background.image.draw(real_x+background.image.width, real_y, background.zorder) end + background.x = save_x end self