indexing description: "[ COLLISION is an example of how to use the collision detection of EiffelMedia. Collision handling was done with simple reflections along the intersection direction, which results in collidables clogging together. This is intended, in order to show that just simply changing the directions of the objects does not suffice, if there are multiple collisions at one given time. ]" date: "$Date$" revision: "$Revision$" class COLLISION inherit EM_APPLICATION EM_TIME undefine make end create make feature {NONE} -- Initialization make is -- Create the main application. local cursor: DS_LINKED_LIST_CURSOR [MOVABLE] i: INTEGER do -- Initialize `video_subsystem' and `screen'. video_subsystem.set_video_surface_width (width) video_subsystem.set_video_surface_height (height) video_subsystem.set_video_bpp (resolution) initialize_screen set_window_icon ("icon.png") set_window_title ("EiffelMedia Collision Detection 2D") set_application_id ("collision_detection_2d") -- 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) -- Event loop. create event_loop.make_poll event_loop.key_down_event.subscribe (agent handle_key_down_event (?)) event_loop.quit_event.subscribe (agent handle_quit_event (?)) event_loop.update_event.subscribe (agent handle_update_event) event_loop.dispatch video_subsystem.disable 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. require a_keyboard_event_not_void: a_keyboard_event /= Void 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. require a_quit_event_not_void: a_quit_event /= Void do event_loop.stop end handle_update_event is -- Handle the outside event. do move_all draw_scene end feature {NONE} -- Implementation event_loop: EM_EVENT_LOOP -- Event loop width: INTEGER is 1024 -- Width of the video surface height: INTEGER is 768 -- Height of video surface resolution: INTEGER is 32 -- Resolution of the video surface 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. end