RMRK is retiring.
Registration is disabled. The site will remain online, but eventually become a read-only archive. More information.

RMRK.net has nothing to do with Blockchains, Cryptocurrency or NFTs. We have been around since the early 2000s, but there is a new group using the RMRK name that deals with those things. We have nothing to do with them.
NFTs are a scam, and if somebody is trying to persuade you to buy or invest in crypto/blockchain/NFT content, please turn them down and save your money. See this video for more information.
Sprite Clipping Method

0 Members and 1 Guest are viewing this topic.

pokeball TDSOffline
***
Rep:
Level 84
-T D S-
Silver - GIAW 11 (Hard)Silver - Game In A Week VII
Yes, more math problems. But this time I think I've improved the way I ask for help.

I've been trying to create a clipping method for a sprite reflection script for a very long time, but the math always seems to be beyond me.

The method would set the rect of a sprite based on the distance between Point A and B, this effect would make it seem like the reflection of the sprite is being cut off as it exits a reflective area into a non reflective area.

For example if a sprite moves from point A (0) to B (32) in 16 frames then it would be moving at 2 pixels per frame (2 * 16 = 32) and it would clip the sprites accordingly based on it's direction. Let's say it's clipping by X and the sprite's width is 64, it would cut 4 pixels from the sprites width per frame until it disappears (4 * 16 = 64).

In theory I think I have it down... But in practice it's another breed of cat all together.

I would like some help in figuring out how to obtain a proper value to use for the distance between a current position and a new one of a moving character. I've spent a long time reading the character scripts and methods and I know exponents are involved, but following the values gives me very mixed results and when I think I've found a good formula changing the speed of the character messes it up.

I'm sure it's probably something simple, but I've been working on this for so long I'm probably blind to the answer by now.

Here's the script I'm working on [VX Ace].

Code: [Select]
#==============================================================================
# ** TDS Sprite Reflection
#    Ver: 1.0
#------------------------------------------------------------------------------
#  * Description:
#
#------------------------------------------------------------------------------
#  * Features:
#
#------------------------------------------------------------------------------
#  * Instructions:
#
#------------------------------------------------------------------------------
#  * Notes:
#  None.
#------------------------------------------------------------------------------
# WARNING:
#
# Do not release, distribute or change my work without my expressed written
# consent, doing so violates the terms of use of this work.
#
# If you really want to share my work please just post a link to the original
# site.
#
# * Not Knowing English or understanding these terms will not excuse you in any
#   way from the consequenses.
#==============================================================================
# * Import to Global Hash *
#==============================================================================
($imported ||= {})[:TDS_Sprite_Reflection] = true

#------------------------------------------------------------------------------
# * Constants
#------------------------------------------------------------------------------
# Reflection Terrain Tag ID
REFLECTION_TERRAIN_TAG = 1
# Reflection Water Wave Effect
REFLECTION_WAVE_EFFECT = true

#==============================================================================
# ** Spriteset_Map
#------------------------------------------------------------------------------
#  This class brings together map screen sprites, tilemaps, etc. It's used
# within the Scene_Map class.
#==============================================================================

class Spriteset_Map
  #--------------------------------------------------------------------------
  # * Alias Listings
  #-------------------------------------------------------------------------- 
  alias tds_sprite_reflection_spriteset_map_initialize             initialize
  alias tds_sprite_reflection_spriteset_map_dispose                dispose
  alias tds_sprite_reflection_spriteset_map_update                 update
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    # Create Character Reflection Sprites
    create_character_reflections
    # Run Original Method
    tds_sprite_reflection_spriteset_map_initialize
  end
  #--------------------------------------------------------------------------
  # * Dispose
  #--------------------------------------------------------------------------
  def dispose
    # Run Original Method
    tds_sprite_reflection_spriteset_map_dispose
    # Dispose of Reflection Sprites
    @reflection_sprites.each {|s| s.dispose}
  end 
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
    # Run Original Method
    tds_sprite_reflection_spriteset_map_update
    # Update Character Reflections
    update_character_reflections
  end
  #--------------------------------------------------------------------------
  # * Create Character Reflection Sprites
  #--------------------------------------------------------------------------
  def create_character_reflections
    # Make Reflection Sprites Array
    @reflection_sprites = []
    # Go Through Game Map Events
    $game_map.events.values.each do |event|
      # If Event has Reflection
      if event.has_reflection?
        # Add Reflection Sprite to Array
        @reflection_sprites << Sprite_Character_Reflection.new(@viewport1, event)
      end
    end

    # Go Through Follower Sprites
    $game_player.followers.reverse_each do |follower|
      # Add Reflection Sprite to Array     
      @reflection_sprites << Sprite_Character_Reflection.new(@viewport1, follower)
    end
    # Add Player Reflection Sprite to Array     
    @reflection_sprites << Sprite_Character_Reflection.new(@viewport1, $game_player)
  end
  #--------------------------------------------------------------------------
  # * Update Character Reflection Sprites
  #--------------------------------------------------------------------------
  def update_character_reflections
    # Update Reflection Sprites
    @reflection_sprites.each {|s| s.update}
  end
end


#==============================================================================
# ** Game_Character
#------------------------------------------------------------------------------
#  A character class with mainly movement route and other such processing added.
#  It is used as a super class of Game_Player, Game_Follower, GameVehicle,
#  and Game_Event.
#==============================================================================

class Game_Character < Game_CharacterBase
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :reflection_active               # Reflection Active Flag
  attr_accessor :reflection_visible              # Reflection Visiblity Flag 
  attr_accessor :reflection_y_offset             # Reflection Y Offset value
  #--------------------------------------------------------------------------
  # * Alias Listings
  #-------------------------------------------------------------------------- 
  alias tds_sprite_reflection_game_character_init_public_members init_public_members   
  #--------------------------------------------------------------------------
  # * Initialize Public Member Variables
  #--------------------------------------------------------------------------
  def init_public_members
    # Set Reflection Active Flag
    @reflection_active = true
    # Set Reflection Visibility Flag
    @reflection_visible = true
    # Set Reflection Y Offset Value
    @reflection_y_offset = 9
    # Run Original Method
    tds_sprite_reflection_game_character_init_public_members
  end
  #--------------------------------------------------------------------------
  # * Determine if Character has a reflection
  #--------------------------------------------------------------------------
  def has_reflection?
    # If Self is an Game_Event Character
    if self.is_a?(Game_Event)
      # Return true if Event Name Includes the Reflect Flag
      return true if /\[REFLECT\]/i =~ @event.name
    end
    # Return false by default
    return false
  end
  #--------------------------------------------------------------------------
  # * Determine if Character reflection is visible
  #--------------------------------------------------------------------------
  def reflection_visible?
    return false if !@reflection_active
    return false if @transparent
    return false if $game_map.terrain_tag(@x, @y + 1) != REFLECTION_TERRAIN_TAG   
    return true
  end
end

#==============================================================================
# ** Sprite_Character
#------------------------------------------------------------------------------
#  This sprite is used to display character reflection. It observes an instance
#  of the Game_Character class and automatically changes sprite state.
#==============================================================================

class Sprite_Character_Reflection < Sprite_Character
  #--------------------------------------------------------------------------
  # * Object Initialization
  #     viewport  : viewport
  #     character : Game_Character
  #--------------------------------------------------------------------------
  def initialize(viewport, character = nil)
    super(viewport, character)
    # Character Ojbect
    @character = character
    # Reflection Sprite Settings
    self.mirror = true ; self.angle = 180 ; self.opacity = 160   
    # Set Self Wave Amp if Reflection Wave Effect is true
    self.wave_amp = 1 if REFLECTION_WAVE_EFFECT
    # Update
    update
  end
  #--------------------------------------------------------------------------
  # * Dispose
  #--------------------------------------------------------------------------
  def dispose ; super end ; def update_balloon ; end ; def setup_new_effect ; end
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
    super
  end
  #--------------------------------------------------------------------------
  # * Update Transfer Origin Rectangle
  #--------------------------------------------------------------------------
  def update_src_rect
   
    # FOR CLIPPING TESTING
    if @character.moving? and (self.visible and !@character.reflection_visible?)
      # CLIP HERE
      return
    end
   
    super
  end
  #--------------------------------------------------------------------------
  # * Update Position
  #--------------------------------------------------------------------------
  def update_position
    self.x = @character.screen_x
    self.y = @character.screen_y + @character.reflection_y_offset
    self.z = @character.screen_z
  end
  #--------------------------------------------------------------------------
  # * Update Other
  #--------------------------------------------------------------------------
  def update_other   
    self.blend_type = @character.blend_type
#    self.visible = @visible = @character.reflection_visible?
  end
end

If I need to explain myself more clearly, please let me know.

Thank you for reading and have a nice day.

*
RMRK's dad
Rep:
Level 86
You know, I think its all gonna be okay.
For going the distance for a balanced breakfast.Project of the Month winner for June 2009For being a noted contributor to the RMRK Wiki2013 Best WriterSilver Writing ReviewerSecret Santa 2013 Participant
Math... ow, my feeble brain...

No help from me, unfortunately, but I applaud your efforts and knowledge.
:tinysmile:

*
Rep:
Level 97
2014 Most Unsung Member2014 Best RPG Maker User - Engine2013 Best RPG Maker User (Scripting)2012 Most Mature Member2012 Favorite Staff Member2012 Best RPG Maker User (Scripting)2012 Best MemberSecret Santa 2012 ParticipantProject of the Month winner for July 20092011 Best Veteran2011 Favourite Staff Member2011 Most Mature Member2011 Best RPG Maker User (Scripting)2011 Best Use of Avatar and Signature Space2010 Most Mature Member2010 Favourite Staff Member
I have to go to work now, but when I get home I will see if I can help.

*
Rep:
Level 85
I solve practical problems.
For taking arms in the name of your breakfast.
umm i only read this quickly but couldn't you get the distance in tiles, convert it to pixels, then divide by the frames per second of a movement speed?

or could you not do it on a tile by tile basis, get the characters x and y directions, check what way their moving, and then evalute everything based on one tile at that characters current move speed

then again, I may just be missing the entire boat on this one XD
« Last Edit: March 09, 2012, 02:33:04 PM by IAMFORTE »

pokeball TDSOffline
***
Rep:
Level 84
-T D S-
Silver - GIAW 11 (Hard)Silver - Game In A Week VII
Yep, that's pretty much it on a nutshell.

The problem is obtaining the correct value that will match all speeds when moving from point A to B. At the normal speed 4 the value of distance_per_frame is 0.625. At the speed of 6 (x4 Faster) the value is 0.25 and at 1 (x8 Slower) it's 0.0078125.

I think that the distance between positions is either 16 or 32 pixels and 256 is used as a real coordinate value for the position on the screen.

Here's the method that calculates the distance per frame used for movement.

Code: [Select]
#==============================================================================
# ** Game_CharacterBase
#------------------------------------------------------------------------------
#  This base class handles characters. It retains basic information, such as
# coordinates and graphics, shared by all characters.
#==============================================================================

class Game_CharacterBase
  #--------------------------------------------------------------------------
  # * Get Move Speed (Account for Dash)
  #--------------------------------------------------------------------------
  def real_move_speed
    @move_speed + (dash? ? 1 : 0)
  end 
  #--------------------------------------------------------------------------
  # * Calculate Move Distance per Frame
  #--------------------------------------------------------------------------
  def distance_per_frame
    2 ** real_move_speed / 256.0
  end
end

And thank you modern algebra, hopefully I will finally be able to put this script down with your help.

*
Rep:
Level 82
Whenever I see distance between points I'm thinking vectors and the math that goes with it. I don't really know how to go about achieving what you want to. At least not without spending some time going over it myself.

To save the hassle, I have a 2d vector snippet that I wrote for my current project that should port across to VXA as is. It might be of help for someone else if they have a good idea. I'll ponder on it in the mean time. I'm still trying to picture the end result and figure out the parts.

Spoiler for Vector2D object:
Code: [Select]
#Creates a 2 Dimensional Vector object
# Can be used to perform various Vector mathematics

class Vector2D
 
  attr_accessor :x
  attr_accessor :y
 
  def initialize(point_x, point_y)
    @x = point_x
    @y = point_y
  end
 
  #returns a new vector object
  #rhs can be a Vector2D or an Array object
  def add(rhs)
    if rhs.is_a?(Vector2D)
      return Vector2D.new(@x + rhs.x, @y + rhs.y)
    elsif rhs.is_a?(Array)
      return Vector2D.new(@x - rhs[0], @y - rhs[1])
    end
  end
 
  #returns a new vector object
  #rhs can be a Vector2D or an Array object
  def subtract(rhs)
    if rhs.is_a?(Vector2D)
      return Vector2D.new(@x - rhs.x, @y - rhs.y)
    elsif rhs.is_a?(Array)
      return Vector2D.new(@x - rhs[0], @y - rhs[1])
    end
  end
 
  #returns a new vector object of self minus rhs
  #rhs can be a Vector2D or an Array object
  def -(rhs)
    return subtract(rhs)
  end
 
  def +(rhs)
    return add(rhs)
  end
 
  #returns the dot product of self and rhs
  def dot(rhs)
    if rhs.is_a?(Vector2D)
      return Float(@x * rhs.x + @y *rhs.y)
    elsif rhs.is_a?(Array)
      return Float(@x * rhs[0] + @y * rhs[1])
    end
  end
 
  #returns the cross product of self and rhs
  #returns a new vector2D
  def cross(rhs)
    if rhs.is_a?(Vector2D)
      return Vector2D.new(@x * rhs.y, @y * rhs.x)
    elsif rhs.is_a?(Array)
      return Vector2D.new(@x * rhs[1], @y * rhs[0])
    end
  end
 
  #scales self by the scalar value
  #returns a new vector2D
  def scale(scalar)
    return Vector2D.new(@x * scalar, @y * scalar)
  end
 
  def *(scalar)
    return scale(scalar)
  end
 
  def length
    return Math.sqrt(dot(self))
  end
 
  #normalizes self
  def normalize!
    normal = normalize
    @x = normal.x; @y = normal.y
  end
 
  #return a new Vector2D that is the normal of self
  def normalize
    normal = scale(1/length)
    return Vector2D.new(normal.x, normal.y)
  end
 
  #reverses self
  def reverse!
    @x = -@x
    @y = -@y
  end
 
  #return a new Vector2D that is the reverse of self
  def reverse
    return Vector2D.new(-@x, -@y)
  end
 
  #converts the x and y of self to absolute values
  def abs!
    @x.abs; @y.abs
  end
 
  #returns the absolute of the vector
  def abs
    return Vector2D.new(@x.abs, @y.abs)
  end
 
  def self.dot(vector_1, vector_2)
    return vector_1.dot(vector_2)
  end
   
end

(Why do I always feel like it's the end of the world and I'm the last man standing?)

pokeball TDSOffline
***
Rep:
Level 84
-T D S-
Silver - GIAW 11 (Hard)Silver - Game In A Week VII
I'll give vectors and your script a good read then, thank you.

I also made a simple example of the clipping effect on loop to show what I am trying to.

Code: [Select]
# Background Grid Sprite
@background = Sprite.new
@background.bitmap = Bitmap.new(Graphics.width, Graphics.height)


for x in 0...(Graphics.width / 32)
  @background.bitmap.fill_rect(x * 32, 0, 1, Graphics.width, Color.new(0, 0, 255))
end

for y in 0...(Graphics.height / 32)
  @background.bitmap.fill_rect(0, y * 32, Graphics.width, 1, Color.new(0, 0, 255))   
end

# Character Sprite
@character = Sprite.new
@character.bitmap = Cache.character("Actor1")
@character.src_rect.set(32, 64, 32, 32)
@character.x = 97 ; @character.y = 64

# Reflection Sprite
@reflection = Sprite.new
@reflection.bitmap = @character.bitmap.dup
@reflection.src_rect.set(32, 64, 32, 32)

@reflection.ox = @reflection.oy = 32
@reflection.mirror = true ; @reflection.angle = 180
@reflection.x = @character.x ; @reflection.y = @character.y + @character.height

loop do
 
  for i in 0...32
    Graphics.update
    Input.update
    @character.x += 1   
    @reflection.x = @character.x   
    @reflection.bitmap.clear_rect(32 + (30 - i), 64, 1, 32)
    Graphics.wait(10) if Input.press?(:C)
  end
  Graphics.wait(60)
  @character.x = 97 ; @reflection.x = @character.x 
  @reflection.bitmap = @character.bitmap.dup   
  @reflection.src_rect.set(32, 64, 32, 32)     
  Graphics.wait(30) 
end
(Pressing C (Enter) slows down the effect)

*****
Rep:
Level 84
This text is way too personal.
Bronze - GIAW 11 (Hard)Silver - GIAW Halloween
If you're worried about varying speeds, you should just use the real_x method and compare it to the characters x value.

Code: [Select]
  #--------------------------------------------------------------------------
  # * Update Transfer Origin Rectangle
  #--------------------------------------------------------------------------
  def update_src_rect
   
    # FOR CLIPPING TESTING
    if @character.moving? and (self.visible and !@character.reflection_visible?)
      # CLIP HERE
      if @tile_id == 0
        index = @character.character_index
        pattern = @character.pattern < 3 ? @character.pattern : 1
        sx = (index % 4 * 3 + pattern) * @cw
        sy = (index / 4 * 4 + (@character.direction - 2) / 2) * @ch
        if @character.x < @character.real_x
          self.src_rect.set(sx + @cw - (@character.x - @character.real_x).abs * @cw, sy, (@character.x - @character.real_x).abs * @cw, @ch)
          self.ox = ((@character.x - @character.real_x).abs * @cw) / 2
        elsif @character.x > @character.real_x
          self.src_rect.set(sx, sy, ((@character.x - @character.real_x) * @cw), @ch)
          self.ox = ((@character.x - @character.real_x).abs * @cw) / 2
        end
      end

      Graphics.wait(20) if Input.press?(:F5) # Debug

      return
    end
   
    self.ox = @cw / 2 # Reset ox
   
    super
  end
  #--------------------------------------------------------------------------
  # * Update Position
  #--------------------------------------------------------------------------
  def update_position
    self.x = @character.screen_x + (@cw / 2 - self.ox) * (@character.real_x < @character.x ? -1 : 1)
    self.y = @character.screen_y + @character.reflection_y_offset
    self.z = @character.screen_z
  end

I'm not quite sure, but is this what you were trying to accomplish?

pokeball TDSOffline
***
Rep:
Level 84
-T D S-
Silver - GIAW 11 (Hard)Silver - Game In A Week VII
Yes, I believe that is exactly what I was looking for.

(@character.x - @character.real_x).abs

The answer seems so simple once someone find it, thank you.

Now to rework it into clipping when exiting and entering reflective areas.

If it isn't too much trouble, could you explain it a bit though? I believe I understand how it works, but I want to be sure that I'm going in the right direction.

(@character.x - @character.real_x).abs is basically working as a percent of distance reached between point A and B and by multiplying it by the width of the sprite you get a size based on the percent/difference between point A and B.

Again thank you very much cozziekuns, and thanks as well to everyone else.

*****
Rep:
Level 84
This text is way too personal.
Bronze - GIAW 11 (Hard)Silver - GIAW Halloween
(@character.x - @character.real_x).abs is basically working as a percent of distance reached between point A and B and by multiplying it by the width of the sprite you get a size based on the percent/difference between point A and B.

Yep, that's pretty much it.