indexing description: "[ Main scene of collision example ]" date: "$Date$" revision: "$Revision$" class COLLISION_SCENE inherit EM_SCENE redefine handle_key_down_event, handle_quit_event, handle_update_event end EM_TIME rename make as initialize_scene undefine initialize_scene, quit end create make feature -- Initialization make (a_screen: EM_VIDEO_SURFACE) is -- create scene do make_scene screen := a_screen end initialize_scene is -- first thing to do local cursor: DS_LINKED_LIST_CURSOR [MOVABLE] i: INTEGER do -- initialize scene objects initialize_movables -- initialize collision_detector -- the first collision detector just detects collisions between all movables create collision_detector.make (movables.count) collision_detector.set_search_depth (search_depth) -- the second collision detector detects collisions between movables and the walls -- only 2 collision groups are needed, because walls don't have to be checked -- with each other, and the movables already have been checked by `collision_detector' create collision_detector_wall.make (2) collision_detector_wall.set_search_depth (search_depth) -- add all movables to the collision detector create cursor.make (movables) from cursor.start i := 1 until cursor.off loop collision_detector.add (cursor.item, i) i := i + 1 cursor.forth end -- add movables and walls to `collision_detector_wall' collision_detector_wall.add (create {EM_RECTANGLE_COLLIDABLE}.make (create {EM_VECTOR_2D}.make (-50, -50), create {EM_VECTOR_2D}.make (width+50, 0)), 1) collision_detector_wall.add (create {EM_RECTANGLE_COLLIDABLE}.make (create {EM_VECTOR_2D}.make (-50, -50), create {EM_VECTOR_2D}.make (0, height+50)), 1) collision_detector_wall.add (create {EM_RECTANGLE_COLLIDABLE}.make (create {EM_VECTOR_2D}.make (width+50, height+50), create {EM_VECTOR_2D}.make (-50, height)), 1) collision_detector_wall.add (create {EM_RECTANGLE_COLLIDABLE}.make (create {EM_VECTOR_2D}.make (width+50, height+50), create {EM_VECTOR_2D}.make (width, -50)), 1) collision_detector_wall.add_list (movables, 2) end feature -- Access collision_detector: EM_COLLISION_DETECTOR [MOVABLE] -- the collision detector collision_detector_wall: EM_COLLISION_DETECTOR [EM_COLLIDABLE] -- the collision detector for the walls -- This is done, to differ from the differenct collision handlings of movables and immobiles. -- The other possibility is to add a variable in the MOVABLE (or whatever it might be called) -- class named `type' or so, which can be checked at time of collision handling, to differ -- all the different types of collidables. feature {NONE} -- Implementation draw_scene is -- Draws the collidables and intersection points on the screen local cursor: DS_LINKED_LIST_CURSOR [MOVABLE] do -- clear screen screen.clear create cursor.make (movables) from cursor.start until cursor.off loop cursor.item.draw (screen) if show_collidables then cursor.item.draw_collidable (screen) end cursor.forth end -- redraw screen screen.redraw end initialize_movables is -- initializes movables local i, j: INTEGER r: RANDOM do create movables.make create r.make r.set_seed (time.ticks) r.start -- add big circles from i := 125 until i > width loop from j := 125 until j > height loop movables.force_last (create {CIRCLE_BIG}.make (create {EM_VECTOR_2D}.make (i,j), 23)) movables.last.set_direction (create {EM_VECTOR_2D}.make (-speed, -speed)) j := j + 300 end i := i + 300 end -- add small circles from i := 50 until i > width loop from j := 50 until j > height loop movables.force_last (create {CIRCLE}.make (create {EM_VECTOR_2D}.make (i,j), 14)) movables.last.set_direction (create {EM_VECTOR_2D}.make (speed, speed)) j := j + 150 end i := i + 150 end -- add rectangle movables.force_last (create {SQUARE}.make (create {EM_VECTOR_2D}.make (525,525), create {EM_VECTOR_2D}.make (625,625))) movables.last.set_direction (create {EM_VECTOR_2D}.make (speed, speed)) end move_all is -- moves all collidables local cursor: DS_LINKED_LIST_CURSOR [MOVABLE] composition_cursor: DS_LINKED_LIST_CURSOR [EM_COLLISION_COMPOSITION [MOVABLE]] composition_cursor2: DS_LINKED_LIST_CURSOR [EM_COLLISION_COMPOSITION [EM_COLLIDABLE]] collision_cursor: DS_LINKED_LIST_CURSOR [EM_COLLISION [MOVABLE]] collision_cursor2: DS_LINKED_LIST_CURSOR [EM_COLLISION [EM_COLLIDABLE]] dir1, dir2, p1, p2: EM_VECTOR_2D a_time, b_time: DOUBLE a_delay: INTEGER a_movable: MOVABLE a_wall: EM_COLLIDABLE do a_delay := (time.ticks - last_time) last_time := time.ticks create cursor.make (movables) from cursor.start until cursor.off loop cursor.item.step_forward (a_delay) cursor.forth end -- now check for collisions and handle them accordingly collision_detector.check_for_collision create composition_cursor.make (collision_detector.last_collisions) -- loop through all collision compositions from composition_cursor.start until composition_cursor.off loop create collision_cursor.make (composition_cursor.item) -- loop through the single collisions from collision_cursor.start until collision_cursor.off loop -- Very simple collision handling - just reflect along the collision axis if collision_cursor.item.collision_point /= Void then -- if there is no inclusion of collidables a_time := collision_cursor.item.time dir1 := collision_cursor.item.collidables.first.direction dir2 := collision_cursor.item.collidables.second.direction dir1 := dir1.reflection (collision_cursor.item.collision_direction.to_vector) dir2 := dir2.reflection (collision_cursor.item.collision_direction.to_vector) -- if time is not 1 (which means a depth search was enabled, to find the FIRST -- collision point), the position is fixed accordingly if a_time < 1 then b_time := (1-a_time) * a_delay / 1000 a_time := a_time * a_delay / 1000 -- set new positions p1 := (collision_cursor.item.collidables.first.direction) * a_time p2 := dir1 * b_time collision_cursor.item.collidables.first.set_center (collision_cursor.item.collidables.first.last_center + p1 + p2) p1 := (collision_cursor.item.collidables.second.center - collision_cursor.item.collidables.second.last_center) * a_time p2 := dir2 * b_time collision_cursor.item.collidables.second.set_center (collision_cursor.item.collidables.second.last_center + p1 + p2) end collision_cursor.item.collidables.first.set_direction (dir1) collision_cursor.item.collidables.second.set_direction (dir2) end collision_cursor.forth end composition_cursor.forth end -- check for collisions with walls collision_detector_wall.check_for_collision create composition_cursor2.make (collision_detector_wall.last_collisions) -- loop through all collision compositions from composition_cursor2.start until composition_cursor2.off loop create collision_cursor2.make (composition_cursor2.item) -- loop through the single collisions from collision_cursor2.start until collision_cursor2.off loop -- Very simple collision handling - just reflect along the collision axis if collision_cursor2.item.collision_point /= Void then -- if there is no inclusion of collidables a_movable ?= collision_cursor2.item.collidables.first if a_movable = Void then -- first was a movable a_movable ?= collision_cursor2.item.collidables.second a_wall := collision_cursor2.item.collidables.first else a_wall := collision_cursor2.item.collidables.second end dir1 := a_movable.direction dir1 := dir1.reflection (collision_cursor2.item.collision_direction.to_vector) a_time := collision_cursor2.item.time -- if time is not 1 (which means a depth search was done, to find the FIRST -- collision point), the position is fixed accordingly if a_time < 1 then b_time := (1-a_time) * a_delay / 1000 a_time := a_time * a_delay / 1000 -- set new positions p1 := (a_movable.direction) * a_time p2 := dir1 * b_time a_movable.set_center (a_movable.last_center + p1 + p2) end a_movable.set_direction (dir1) end collision_cursor2.forth end composition_cursor2.forth end end feature -- Event handling handle_key_down_event (a_keyboard_event: EM_KEYBOARD_EVENT) is -- Handle keyboard events. do if a_keyboard_event.key = a_keyboard_event.sdlk_escape then event_loop.stop end if a_keyboard_event.key = a_keyboard_event.sdlk_f1 then show_collidables := not show_collidables end end handle_quit_event (a_quit_event: EM_QUIT_EVENT) is -- Handle quit events. do event_loop.stop end handle_update_event is -- Handle the outside event. do move_all draw_scene end feature -- Drawing redraw is -- Make feature `redraw' effective do end feature {NONE} -- Implementation movables: DS_LINKED_LIST [MOVABLE] -- a list of all movables last_time: INTEGER -- time.ticks of the last frame show_collidables: BOOLEAN -- disabled by default. press f1 to toggle speed: DOUBLE is 100.0 -- speed of the initial objecs search_depth: INTEGER is 0 -- the depth of search of the first collision point. is able to find -- collisions in between steps and returns the time of collision between 0 and 1 -- if the value is 0, it is disabled. width: INTEGER is -- do Result := screen.width end height: INTEGER is -- do Result := screen.height end end