Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

sprite coordinates

Hi,

It has been so long since I have not posted anything here. But I have never stopped scripting. I am currently creating a game engine The player can move in any direction pixel by pixel.
I added a function to the Sprite class which determines the coordinates of the vertices of a sprite based on the angle of rotation.


[ruby]class Sprite
  attr_accessor :rectangle
 
  alias :rectangle_initialize :initialize unless $@
  alias :rectangle_angle :angle= unless $@
 
  def initialize(*args)
    rectangle_initialize(*args)
    @rectangle=Rectangle.new
  end
 
  def angle=(a)
    rectangle_angle(a)
    c=Math.cos(r=a*PI/180.0)
    s=Math.sin(r)
   
    hs=self.bitmap.height*s/2
    ls=self.bitmap.width*s/2
    hc=self.bitmap.height*c/2
    lc=self.bitmap.width*c/2
   
    @rectangle.a.set(self.x-lc-hs,self.y-hc+ls)
    @rectangle.b.set(self.x+lc-hs,self.y-hc-ls)
    @rectangle.b.set(self.x+lc-hs,self.y-hc-ls)
    @rectangle.c.set(self.x+lc+hs,self.y+hc-ls)
    @rectangle.d.set(self.x-lc+hs,self.y+hc+ls)
  end
end
[/ruby]

Note: Rectangle#a=[x,y]

This code works when Sprite#ox and Sprite#oy are the center of the sprite.
How to calculate the coordinates as ox and oy are not the center ? (e.g: ox = self.bitmap.width/5)

Trigonometry is so far :sad:

Thanks in advance,
Regards,

Berka
 
Still working on simplifying my formula for this. Basically, for any arbitrary point [ox, oy], you are finding:

- Distances from each point of original rect to [ox, oy].
- An angle value back to 0.

Then adding your new sprite.angle to each of those angles and re-calculating each rectangle point based off the hypot (distance from offset) and the original angle + new angle.


The only part that is making this tricky is say

: +ox, +oy
: +ox, -oy
: -ox, +oy
: -ox, -oy

I haven't had time to sit down yet and work on this, but should sometime in the next 12 hours.
 
Wow been out of Trig to long. Forgot a valuable formula:

(x1, y1) = ((x*cos(theta) - y*sin(theta)), (x*sin(theta) + y*cos(theta)))

So here is what I came up with:
Code:
module Math

  def self.degrees_to_radians(d)

    return d * PI / 180

  end

  def self.radians_to_degrees(r)

    return r * 180 / PI

  end

  @q_cos = {}

  @q_sin = {}

  for i in 0...360

    @q_cos[i] = self.cos(self.degrees_to_radians(i))

    @q_sin[i] = self.sin(self.degrees_to_radians(i))

  end

  def self.q_cos(i, degrees = true)

    i = self.radians_to_degrees(i) unless degrees

    return @q_cos[i.round % 360]

  end

  def self.q_sin(i, degrees)

    i = self.radians_to_degrees(i) unless degrees

    return @q_sin[i.round % 360]

  end

end

 

class Rotating_Rect

  attr_accessor :angle

  attr_accessor :a, :b, :c, :d

  def initialize

    @angle = 0

    @a, @b, @c, @d = [], [], [], []

  end

  def rotate_points(x, y, angle)

    ax = @a[0] - x ; ay = @a[1] - y

    bx = @b[0] - x ; by = @b[1] - y

    cx = @c[0] - x ; cy = @c[1] - y

    dx = @d[0] - x ; dy = @d[1] - y

    nax = ax * Math.q_cos(angle) - ay * Math.q_sin(angle)

    nay = ax * Math.q_sin(angle) - ay * Math.q_cos(angle)

    nbx = bx * Math.q_cos(angle) - by * Math.q_sin(angle)

    nby = bx * Math.q_sin(angle) - by * Math.q_cos(angle)

    ncx = cx * Math.q_cos(angle) - cy * Math.q_sin(angle)

    ncy = cx * Math.q_sin(angle) - cy * Math.q_cos(angle)

    ndx = dx * Math.q_cos(angle) - dy * Math.q_sin(angle)

    ndy = dx * Math.q_sin(angle) - dy * Math.q_cos(angle)

    @angle = angle

    @a = [nax + x, nay + y]

    @b = [nbx + x, nby + y]

    @c = [ncx + x, ncy + y]

    @d = [ndx + x, ndy + y]  

  end

end

 

class Sprite

  attr_reader :angle_rect

  alias_method :seph_spriterects_init, :initialize

  def initialize(*args)

    seph_spriterects_init(*args)

    @angle_rect = Rotating_Rect.new

    @angle_rect.a = [0, 0]

    @angle_rect.b = [self.bitmap.width, 0]

    @angle_rect.c = [0, self.bitmap.height]

    @angle_rect.d = [self.bitmap.width, self.bitmap.height]

  end

  alias_method :seph_spriterects_setbitmap, :bitmap=

  def bitmap=(bitmap)

    seph_spriterects_setbitmap(bitmap)

    @angle_rect.angle = 0

    @angle_rect.a = [0, 0]

    @angle_rect.b = [self.bitmap.width, 0]

    @angle_rect.c = [0, self.bitmap.height]

    @angle_rect.d = [self.bitmap.width, self.bitmap.height]

    @angle_rect.rotate_points(self.ox, self.oy, self.angle)

  end

  alias_method :seph_spriterects_setangle, :angle=

  def angle=(angle)

    seph_spriterects_setangle(angle)

    @angle_rect.angle = 0

    @angle_rect.a = [0, 0]

    @angle_rect.b = [self.bitmap.width, 0]

    @angle_rect.c = [0, self.bitmap.height]

    @angle_rect.d = [self.bitmap.width, self.bitmap.height]

    @angle_rect.rotate_points(self.ox, self.oy, angle)

  end

end

The Rotating_Rect class holds the 4 points of the rect. I put the function for rotating the rect counter-clockwise in that class. That's where the Math is.

Hope this helps you.
 
It shouldn't calculate every frame, unless you are changing your angle instance in your sprite. If that is the case, you can cache the coordinates like so:

Code:
module Math

  def self.degrees_to_radians(d)

    return d * PI / 180

  end

  def self.radians_to_degrees(r)

    return r * 180 / PI

  end

  @q_cos = {}

  @q_sin = {}

  for i in 0...360

    @q_cos[i] = self.cos(self.degrees_to_radians(i))

    @q_sin[i] = self.sin(self.degrees_to_radians(i))

  end

  def self.q_cos(i, degrees = true)

    i = self.radians_to_degrees(i) unless degrees

    return @q_cos[i.round % 360]

  end

  def self.q_sin(i, degrees)

    i = self.radians_to_degrees(i) unless degrees

    return @q_sin[i.round % 360]

  end

end

 

class Rotating_Rect

  attr_accessor :angle

  attr_reader   :ox, :oy

  def initialize

    @angle, @ox, @oy = 0, 0, 0

    @a, @b, @c, @d = [], [], [], []

  end

  def a

    return @values[['a', @angle]]

  end

  def b

    return @values[['b', @angle]]

  end

  def c

    return @values[['c', @angle]]

  end

  def d

    return @values[['d', @angle]]

  end

  def set(a, b, c, d, ox, oy)

    @a, @b, @c, @d, @ox, @oy = a, b, c, d, ox, oy

    set_values

  end

  def set_values

    @values = {}

    keys = {'a' => @a, 'b' => @b, 'c' => @c, 'd' => @d}

    keys.each do |l, instance|

      for i in 0...360

        key = [l, i]

        @values[key] = get_value(@a, i)

        Graphics.update

      end

    end

  end

  def get_value(pos, angle)

    x, y = *pos

    x1, y1 = x - @ox, y - @oy

    x2 = x1 * Math.q_cos(angle) - y1 * Math.q_sin(angle)

    y2 = x1 * Math.q_sin(angle) + y1 * Math.q_cos(angle)

    return [x2 + @ox, y2 + @oy]

  end

end

 

class Sprite

  attr_reader :angle_rect

  alias_method :seph_spriterects_init, :initialize

  def initialize(*args)

    seph_spriterects_init(*args)

    @angle_rect = Rotating_Rect.new

  end

  alias_method :seph_spriterects_setbitmap, :bitmap=

  def bitmap=(bitmap)

    seph_spriterects_setbitmap(bitmap)

    set_angle_rect

  end

  alias_method :seph_spriterects_setox, :ox=

  def ox=(ox)

    seph_spriterects_setox(ox)

    set_angle_rect

  end

  alias_method :seph_spriterects_setoy, :oy=

  def oy=(oy)

    seph_spriterects_setoy(oy)

    set_angle_rect

  end

  alias_method :seph_spriterects_setangle, :angle=

  def angle=(angle)

    seph_spriterects_setangle(angle)

    @angle_rect.angle = self.angle

  end

  def set_angle_rect

    if bitmap != nil

      a = [@x, @y]

      b = [@x + bitmap.width, @y]

      c = [@x, @y + bitmap.height]

      d = [@x + bitmap.width, @y + bitmap.height]

      @angle_rect.set(a, b, c, d, self.ox, self.oy)

      @angle_rect.angle = self.angle

    end

  end

end

Now when the bitmap, ox or oy are set, it calculates all the a-d coordinates for all angles 0 to 359. Now the a-d methods are dynamic and return a cache value determined by the angle. Also before the coordinates assumed that the sprites @x and @y were 0. Now it takes into consideration of the sprite's position (see set_angle_rect).

Again, hope that helps.
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top