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].
#==============================================================================
# ** 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.
Math... ow, my feeble brain...
No help from me, unfortunately, but I applaud your efforts and knowledge.
I have to go to work now, but when I get home I will see if I can help.
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
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.
#==============================================================================
# ** 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.
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=Vector2D object]
#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
[/spoiler]
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.
# 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)
If you're worried about varying speeds, you should just use the real_x method and compare it to the characters x value.
#--------------------------------------------------------------------------
# * 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?
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.
Quote from: TDS on March 12, 2012, 08:47:47 AM
(@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.