#=============================================================================== # [ACE] Advanced Collision Detection v1.2 # by ???nOBodY??? # Last Updated: 4-18-15 #=============================================================================== # # Update History: # - Version 1.2 - Fixed pixel-based collisions for characters # - Version 1.1 - Added arguments to collide_with? method for optimizing function # - Added collision_between? method # - Added picture_collision? method # - Added picture_collide_with_char? method # - Version 1.0 - Initial release # #=============================================================================== # *if you use this script, you must give credits to the following: # -Glitchfinder, from HBGames.org, for explanation on using C++ .dlls in RGSS # -poccil, from HBGames.org, for explanation on RGSS bitmap internal data structure # -Tsukihime, from both rpgmakerweb.com & rpgmakervxace.net, for additional .dll help # -???nOBodY???, from both rpgmakervx.net & rpgmakervxace.net # *this script requires the CollisionDetection.dll to be in the project root directory #=============================================================================== # Features: # -Pixel-based Collision Detection # -Rectangle-based Collision Detection #=============================================================================== # # One of RMVX's greatest limitations is that of its limited bitmaps. # Simply put, bitmaps cannot be used within RMVX's api, for pixel collision # detections. Why? It's far too slow to be of any use. Which is where DLLs # come in. # #=============================================================================== # # Instructions (Using C++ Dlls in RGSS): # # First you have to get a properly compiled .dll file. # Then you'll do the following: # # dllcall = Win32API.new("dll name", "function name", "LlIiNnPp", "LlIiNnPpVv") # dllcall.call(arg1, arg2...) # # Let me explain. # # "dll name": the name of the dll file, with or without the extension. # (case insensitive, crashes if not found) # "function name": The name of the function you wish to call. # (case sensitive, crashes if not found) # "LlIiNnPp": The arguments. This is a string of characters, which # are explained below. # "LlIiNnPpVv": The return value. A single character, follows the # same rules as the arguments. # # Arguments: # "L" or "l", "N" or "n": A long number. This can be used to pass booleans, # where 0 is false and anything else is true. # "I" or "i": An int. This can be used to pass booleans. (see above) # "P" or "p": A pointer to a string. With more advanced programming, can be used # to pass other variable types, such as structures. # "V" or "v": Void. May only be used as a return value. # # One other thing I would like to note is that if the dll crashes, # so will RMXP or RMVX, and that you will receive a non-specific # error message regarding the event. #=============================================================================== # # collide_with?(eventID,arg1,arg2,arg3,arg4,arg5,mode) # Where eventID is the event id of the event to check for collision with the # current event; by default, set for pixel collision detection. # Where arg1 is the minimum R value required for both bitmaps' overlapping pixels # to count as overlap. # Where arg2 is the minimum G value required for both bitmaps' overlapping pixels # to count as overlap. # Where arg3 is the minimum B value required for both bitmaps' overlapping pixels # to count as overlap. # Where arg4 is the minimum A value required for both bitmaps' overlapping pixels # to count as overlap. [currently does nothing] # Where arg5 is the minimum amount of overlaps required to count as a collision. # Where mode is an integer that determines whether to use an event's graphic, or # a "snapshot" of its current animation, if any, for the collision; # 1 #1=>ev 2=>ev || 2 #1=>ev 2=>anime || 3 #1=>anime 2=>ev || 4 #1=>anime 2=>anime # # collision_between?(eventID1,eventID2,arg1,arg2,arg3,arg4,arg5,mode) # Where eventID1 is the event id of the first event to check for collision. # Where eventID2 is the event id of the second event to check for collision. # Where arg1 is (same as above). # Where arg2 is (same as above). # Where arg3 is (same as above). # Where arg4 is (same as above). # Where arg5 is (same as above). # Where mode is an integer that determines whether to use an event's graphic, or # a "snapshot" of its current animation, if any, for the collision; # 1 #1=>ev 2=>ev || 2 #1=>ev 2=>anime || 3 #1=>anime 2=>ev || 4 #1=>anime 2=>anime # # picture_collision?(picID1,picID2,arg1,arg2,arg3,arg4,arg5) # Where picID1 is the id of the first picture (Game_Picture). # Where picID2 is the id of the second picture (Game_Picture). # Where arg1 is (same as above). # Where arg2 is (same as above). # Where arg3 is (same as above). # Where arg4 is (same as above). # Where arg5 is (same as above). # # picture_collide_with_char?(picID,charID,arg1,arg2,arg3,arg4,arg5,mode) # Where picID is the id of the picture (Game_Picture). # Where charID is the character id; 0 for player. # Where arg1 is (same as above). # Where arg2 is (same as above). # Where arg3 is (same as above). # Where arg4 is (same as above). # Where arg5 is (same as above). # Where mode is an integer that determines whether to use an event's graphic, or # a "snapshot" of its current animation, if any, for the collision; # 1 #1=>picture 2=>ev || 2 #1=>picture 2=>anime # # get_eventAnime_spriteset(evID) # Where evID is the event to attempt to get current frame of animation as sprite. # #=============================================================================== module CollisionDetection #Bmp_checkIntersection(long object1, long object2, # int x1, int y1, int x2, int y2, # int minR, int minG, int minB, int minA, int minOverlaps) Bmp_checkIntersection = Win32API.new("CollisionDetection.dll", "Bmp_checkIntersection", "LLiiiiIIIII", "I") Rect_checkIntersection = Win32API.new("CollisionDetection.dll", "Rect_checkIntersection", "iiiiIIIIii", "l") Rect_getIntersectionX = Win32API.new("CollisionDetection.dll", "Rect_getIntersectionX", "iiiiIIII", "I") Rect_getIntersectionY = Win32API.new("CollisionDetection.dll", "Rect_getIntersectionY", "iiiiIIII", "I") Rect_getIntersectionW = Win32API.new("CollisionDetection.dll", "Rect_getIntersectionW", "iiiiIIII", "I") Rect_getIntersectionH = Win32API.new("CollisionDetection.dll", "Rect_getIntersectionH", "iiiiIIII", "I") end # # Rect # Added some intersection functions # class Rect # # Checks if self intersect with other # other: Other Rect # def intersect?(other,minW=2,minH=2) CollisionDetection::Rect_checkIntersection.call(self.x,self.y,self.width,self.height, other.x,other.y,other.width,other.height, minW,minH) end # # Gets intersection rect with other rect # other: Other Rect # def intersection(other) x = CollisionDetection::Rect_getIntersectionX.call(self.x,self.y,self.width,self.height,other.x,other.y,other.width,other.height) y = CollisionDetection::Rect_getIntersectionY.call(self.x,self.y,self.width,self.height,other.x,other.y,other.width,other.height) w = CollisionDetection::Rect_getIntersectionW.call(self.x,self.y,self.width,self.height,other.x,other.y,other.width,other.height) h = CollisionDetection::Rect_getIntersectionH.call(self.x,self.y,self.width,self.height,other.x,other.y,other.width,other.height) ret_rect = Rect.new(x,y,w,h) return ret_rect end end #~ # #~ # Checks pixel collision between two bitmaps, with given padding and max sizes #~ # #~ def self.normal_collide?(_bmp1, s1bpx, s1bpy, _bmp2, s2bpx, s2bpy, iw, ih) #~ return false if iw <= 0 or ih <= 0 or s1bpx >= _bmp1.width or s1bpy >= _bmp1.height or s2bpx >= _bmp2.width or s2bpy >= _bmp2.height #~ !CollisionDetection::Bitmap_normal_collide_check.call([_bmp1.object_id, s1bpx, s1bpy, _bmp2.object_id, s2bpx, s2bpy, iw, ih]).zero? #~ end # # Sprite # Added functions the check if one sprite collide with other # class Sprite # # Simple verification, without zoom or rotation # other: Other Sprite # def pixel_collide?(other,arg1,arg2,arg3,arg4,arg5,type,mode=1) #----- bb1 = Rect.new( self.x - self.ox, self.y - self.oy, self.src_rect.width, self.src_rect.height ) #----- bb2 = Rect.new( other.x - other.ox, other.y - other.oy, other.src_rect.width, other.src_rect.height ) #----- intersection = bb1.intersection(bb2) if intersection.width == 0 or intersection.height == 0 return false else #return true #----- s1bpx = self.x#+self.ox s1bpy = self.y#+self.oy s2bpx = other.x#+other.ox s2bpy = other.y#+other.oy #----- bmp1 = self.bitmap#.clone bmp2 = other.bitmap#.clone case mode when 1 #1=>ev 2=>ev mode1=1 mode2=1 when 2 #1=>ev 2=>anime mode1=1 mode2=2 when 3 #1=>anime 2=>ev mode1=2 mode2=1 when 4 #1=>anime 2=>anime mode1=2 mode2=2 else #if mode is nil mode1=1 mode2=1 end #MODE # 1 #1=>ev 2=>ev || # 2 #1=>ev 2=>anime || # 3 #1=>anime 2=>ev || # 4 #1=>anime 2=>anime # ** type1=1,2,3,4; type2=nil; type3=1,2 temp = false case type when 1 #2 characters if mode1 == 1 name = self.character.character_name return false if name == nil sign = name[/^[\!\$]./] if sign != nil and sign.include?('$') cw = bmp1.width / 3 ch = bmp1.height / 4 else cw = bmp1.width / 12 ch = bmp1.height / 8 end bmpW = cw bmpH = ch n = self.character.character_index pattern = self.character.pattern < 3 ? self.character.pattern : 1 sx = (n % 4 * 3 + pattern) * cw sy = (n / 4 * 4 + (self.character.direction - 2) / 2) * ch rect = Rect.new(sx, sy, cw, ch) #rect = Rect.new(n % 3 * bmpW, n / 3 * bmpW, bmpW, bmpH) bmp1EX = Bitmap.new(bmpW,bmpH) bmp1EX.blt(0, 0, bmp1, rect, 255) s1bpx = self.x-self.ox s1bpy = self.y-self.oy else #animation end #if mode1 == 1 if mode2 == 1 name = other.character.character_name return false if name == nil sign = name[/^[\!\$]./] if sign != nil and sign.include?('$') cw = bmp2.width / 3 ch = bmp2.height / 4 else cw = bmp2.width / 12 ch = bmp2.height / 8 end bmpW = cw bmpH = ch n = other.character.character_index pattern = other.character.pattern < 3 ? other.character.pattern : 1 sx = (n % 4 * 3 + pattern) * cw sy = (n / 4 * 4 + (other.character.direction - 2) / 2) * ch rect = Rect.new(sx, sy, cw, ch) #rect = Rect.new(n % 3 * bmpW, n / 3 * bmpW, bmpW, bmpH) bmp2EX = Bitmap.new(bmpW,bmpH) bmp2EX.blt(0, 0, bmp2, rect, 255) s2bpx = other.x-other.ox s2bpy = other.y-other.oy else #animation end #if mode2 == 1 check=CollisionDetection::Bmp_checkIntersection.call(bmp1EX.__id__,bmp2EX.__id__, s1bpx,s1bpy,s2bpx,s2bpy, arg1,arg2,arg3,arg4,arg5) temp = check == 0 ? false : true when 2 #2 pictures #no interference needed check=CollisionDetection::Bmp_checkIntersection.call(bmp1.__id__,bmp2.__id__, s1bpx,s1bpy,s2bpx,s2bpy, arg1,arg2,arg3,arg4,arg5) temp = check == 0 ? false : true #no interference needed when 3 #1 picture, 1 character if mode2 == 1 name = other.character.character_name return false if name == nil sign = name[/^[\!\$]./] if sign != nil and sign.include?('$') cw = bmp2.width / 3 ch = bmp2.height / 4 else cw = bmp2.width / 12 ch = bmp2.height / 8 end bmpW = cw bmpH = ch n = other.character.character_index pattern = other.character.pattern < 3 ? other.character.pattern : 1 sx = (n % 4 * 3 + pattern) * cw sy = (n / 4 * 4 + (other.character.direction - 2) / 2) * ch rect = Rect.new(sx, sy, cw, ch) #rect = Rect.new(n % 3 * bmpW, n / 3 * bmpW, bmpW, bmpH) bmp2EX = Bitmap.new(bmpW,bmpH) bmp2EX.blt(0, 0, bmp2, rect, 255) s2bpx = other.x-other.ox s2bpy = other.y-other.oy else #animation end check=CollisionDetection::Bmp_checkIntersection.call(bmp1.__id__,bmp2EX.__id__, s1bpx,s1bpy,s2bpx,s2bpy, arg1,arg2,arg3,arg4,arg5) temp = check == 0 ? false : true end #----- return temp end #if intersection.width == 0 or intersection.height == 0 #----- end #def pixel_collide?(other,arg1,arg2,arg3,arg4,arg5,type) end # # Scene_Map # Extented with functions to get event/player and picture spriteset # class Scene_Map < Scene_Base # # Gets Event/Player Animation Spriteset by ID # if called with ID 0, returns the player spriteset # def get_eventAnime_spriteset(evt_id) if evt_id == 0 @spriteset.character_sprites.each do |cspr| return cspr.getAnimationSprite if cspr.character == $game_player && cspr.animation != nil end else @spriteset.character_sprites.each do |cspr| return cspr.getAnimationSprite if cspr.character.id == evt_id && cspr.animation != nil end end nil end # # Gets Event/Player Spriteset by ID # if called with ID 0, returns the player spriteset # def get_event_spriteset(evt_id) if evt_id == 0 @spriteset.character_sprites.each do |cspr| return cspr if cspr.character == $game_player end else @spriteset.character_sprites.each do |cspr| return cspr if cspr.character.id == evt_id end end nil end # # Gets the picture sprite that correspond to pic_id # def get_picture_spriteset(pic_id) @spriteset.picture_sprites.each do |pspr| next unless pspr return pspr if pspr.picture.number == pic_id end nil end end #============================================================================== # ** Sprite_Base #------------------------------------------------------------------------------ # A sprite class with animation display processing added. #============================================================================== class Sprite_Base < Sprite attr_reader :animation #-------------------------------------------------------------------------- # * Get Animation Sprite #-------------------------------------------------------------------------- def getAnimationSprite if @animation_duration > 0 frame_index = @animation.frame_max - (@animation_duration + 3) / 4 return getAnimationSpriteFrame(@animation.frames[frame_index]) else return nil end end #-------------------------------------------------------------------------- # * Get Animation Sprite from current frame # frame : Frame data (RPG::Animation::Frame) #-------------------------------------------------------------------------- def getAnimationSpriteFrame(frame) cell_data = frame.cell_data for i in 0..15 sprite = @animation_sprites[i] next if sprite == nil pattern = cell_data[i, 0] if pattern == nil or pattern == -1 sprite.visible = false next end if pattern < 100 sprite.bitmap = @animation_bitmap1 else sprite.bitmap = @animation_bitmap2 end #sprite.visible = true sprite.src_rect.set(pattern % 5 * 192, pattern % 100 / 5 * 192, 192, 192) if @animation_mirror sprite.x = @animation_ox - cell_data[i, 1] sprite.y = @animation_oy + cell_data[i, 2] sprite.angle = (360 - cell_data[i, 4]) sprite.mirror = (cell_data[i, 5] == 0) else sprite.x = @animation_ox + cell_data[i, 1] sprite.y = @animation_oy + cell_data[i, 2] sprite.angle = cell_data[i, 4] sprite.mirror = (cell_data[i, 5] == 1) end sprite.z = self.z + 300 + i #sprite.ox = 96 #sprite.oy = 96 sprite.zoom_x = cell_data[i, 3] / 100.0 sprite.zoom_y = cell_data[i, 3] / 100.0 sprite.opacity = cell_data[i, 6] * self.opacity / 255.0 sprite.blend_type = cell_data[i, 7] return sprite end end end #============================================================================== # ** Sprite_Character #------------------------------------------------------------------------------ # This sprite is used to display characters. It observes a instance of the # Game_Character class and automatically changes sprite conditions. #============================================================================== class Sprite_Character < Sprite_Base attr_reader :character end #============================================================================== # ** Sprite_Picture #------------------------------------------------------------------------------ # This sprite is used to display picturea. It observes a instance of the # Game_Picture class and automatically changes sprite conditions. #============================================================================== class Sprite_Picture < Sprite attr_reader :picture end #============================================================================== # ** Spriteset_Map #------------------------------------------------------------------------------ # This class brings together map screen sprites, tilemaps, etc. It's used # within the Scene_Map class. #============================================================================== class Spriteset_Map attr_reader :character_sprites attr_reader :picture_sprites end # # Game_Interpreter # Added helper functions to check characters and picture pixel collisions # class Game_Interpreter # # Check collion between active event and other event # o_evt_id: ID of the other event, 0 represent the player ID # def collide_with?(o_evt_id,arg1=5,arg2=5,arg3=5,arg4=64,arg5=8,mode=1,type=1) return false if o_evt_id < 0 or !SceneManager.scene_is?(Scene_Map) case mode when 1 #1=>ev 2=>ev return false unless (o_evt_spr = SceneManager.scene.get_event_spriteset(o_evt_id)) evt_spr = SceneManager.scene.get_event_spriteset(@event_id) evt_spr.pixel_collide?(o_evt_spr,arg1,arg2,arg3,arg4,arg5,type,mode) when 2 #1=>ev 2=>anime return false unless (o_evt_spr = SceneManager.scene.get_eventAnime_spriteset(o_evt_id)) evt_spr = SceneManager.scene.get_event_spriteset(@event_id) evt_spr.pixel_collide?(o_evt_spr,arg1,arg2,arg3,arg4,arg5,type,mode) when 3 #1=>anime 2=>ev return false unless (o_evt_spr = SceneManager.scene.get_event_spriteset(o_evt_id)) evt_spr = SceneManager.scene.get_eventAnime_spriteset(@event_id) return false if evt_spr.nil? evt_spr.pixel_collide?(o_evt_spr,arg1,arg2,arg3,arg4,arg5,type,mode) when 4 #1=>anime 2=>anime return false unless (o_evt_spr = SceneManager.scene.get_eventAnime_spriteset(o_evt_id)) evt_spr = SceneManager.scene.get_eventAnime_spriteset(@event_id) return false if evt_spr.nil? evt_spr.pixel_collide?(o_evt_spr,arg1,arg2,arg3,arg4,arg5,type,mode) end end # # Check collion between two events # evt_id1: ID of a event # evt_id2: ID of other event # 0 represent the player ID # def collision_between?(evt_id1,evt_id2,arg1=5,arg2=5,arg3=5,arg4=64,arg5=8,mode=1,type=1) return true if evt_id1 == evt_id2 return false if evt_id1 < 0 or evt_id2 < 0 or !SceneManager.scene_is?(Scene_Map) case mode when 1 #1=>ev 2=>ev evt_spr1 = SceneManager.scene.get_event_spriteset(evt_id1) evt_spr2 = SceneManager.scene.get_event_spriteset(evt_id2) return false unless evt_id1 and evt_id2 evt_spr1.pixel_collide?(evt_spr2,arg1,arg2,arg3,arg4,arg5,type,mode) when 2 #1=>ev 2=>anime evt_spr1 = SceneManager.scene.get_event_spriteset(evt_id1) evt_spr2 = SceneManager.scene.get_eventAnime_spriteset(evt_id2) return false unless evt_id1 and evt_id2 evt_spr1.pixel_collide?(evt_spr2,arg1,arg2,arg3,arg4,arg5,type,mode) when 3 #1=>anime 2=>ev evt_spr1 = SceneManager.scene.get_eventAnime_spriteset(evt_id1) evt_spr2 = SceneManager.scene.get_event_spriteset(evt_id2) return false unless evt_id1 and evt_id2 evt_spr1.pixel_collide?(evt_spr2,arg1,arg2,arg3,arg4,arg5,type,mode) when 4 #1=>anime 2=>anime evt_spr1 = SceneManager.scene.get_eventAnime_spriteset(evt_id1) evt_spr2 = SceneManager.scene.get_eventAnime_spriteset(evt_id2) return false unless evt_id1 and evt_id2 evt_spr1.pixel_collide?(evt_spr2,arg1,arg2,arg3,arg4,arg5,type,mode) end end # # Check collion between two pictures # pic1_id: ID of a picture # pic2_id: ID of other picture # def picture_collision?(pic1_id,pic2_id,arg1=5,arg2=5,arg3=5,arg4=64,arg5=8,type=2) return true if pic1_id == pic2_id return false if pic1_id <= 0 or pic2_id <= 0 or !SceneManager.scene_is?(Scene_Map) pic1_spr = SceneManager.scene.get_picture_spriteset(pic1_id) pic2_spr = SceneManager.scene.get_picture_spriteset(pic2_id) return false unless pic1_spr and pic2_spr pic1_spr.pixel_collide?(pic2_spr,arg1,arg2,arg3,arg4,arg5,type) end # # Check collion between a picture and an character(event or player) # pic_id: ID of the picture # char_id: ID of the character # 0 represent the player ID # def picture_collide_with_char?(pic_id,char_id,arg1=5,arg2=5,arg3=5,arg4=64,arg5=8,mode=1,type=3) return false if pic_id <= 0 or char_id < 0 or !SceneManager.scene_is?(Scene_Map) pic_spr = SceneManager.scene.get_picture_spriteset(pic_id) case mode when 1 char_spr = SceneManager.scene.get_event_spriteset(char_id) when 2 char_spr = SceneManager.scene.get_eventAnime_spriteset(char_id) end return false unless pic_spr and char_spr return false if pic_spr.bitmap.nil? or char_spr.bitmap.nil? #p pic_spr.picture_name + " || " + char_spr.character_name pic_spr.pixel_collide?(char_spr,arg1,arg2,arg3,arg4,arg5,type,mode) end end