Class: SFML::TextureAtlas

Inherits:
Object
  • Object
show all
Defined in:
lib/sfml/graphics/texture_atlas.rb

Overview

A sprite-sheet plus a frame-name → rectangle index. Loads the JSON descriptors that Aseprite and TexturePacker export — both use the same frames + meta shape, just with a few field differences we paper over.

atlas = SFML::TextureAtlas.load("hero.json")

atlas.texture                  #=> SFML::Texture
atlas.region("hero-walk-0")    #=> SFML::Rect
atlas.frame_names              #=> [...]
atlas.sprite("hero-walk-0")    #=> ready-to-draw SFML::Sprite
atlas.duration("hero-walk-0")  #=> Integer ms (Aseprite only)

The image path inside the JSON is resolved relative to the JSON file's directory. Override with image: "path/to/img.png".

Frame lookups are tolerant of the .png extension — Aseprite exports walk-0.png, but you can write atlas.region("walk-0").

Constant Summary collapse

IMAGE_EXTS =

File-extension regex for atlas image files.

/\.(?:png|jpg|jpeg|bmp|tga|gif)\z/i

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(texture:, regions:, durations: {}, source: nil) ⇒ TextureAtlas

Build directly from a Texture + a frame Hash — useful when generating atlases procedurally or when the descriptor lives in a different format you've already parsed.



43
44
45
46
47
48
# File 'lib/sfml/graphics/texture_atlas.rb', line 43

def initialize(texture:, regions:, durations: {}, source: nil)
  @texture   = texture
  @regions   = regions.transform_keys { |k| _normalize(k) }.freeze
  @durations = durations.transform_keys { |k| _normalize(k) }.freeze
  @source    = source
end

Instance Attribute Details

#source ⇒ Object (readonly)

The texture, source components.



51
52
53
# File 'lib/sfml/graphics/texture_atlas.rb', line 51

def source
  @source
end

#texture ⇒ Object (readonly)

The texture, source components.



51
52
53
# File 'lib/sfml/graphics/texture_atlas.rb', line 51

def texture
  @texture
end

Class Method Details

.load(json_path, image: nil, smooth: true) ⇒ TextureAtlas

Parameters:

  • json_path (String) —

    path to the atlas JSON

  • image (String, nil) (defaults to: nil) —

    explicit image path (overrides the path embedded in the JSON's meta.image field)

Returns:

Raises:



30
31
32
33
34
35
36
37
38
# File 'lib/sfml/graphics/texture_atlas.rb', line 30

def self.load(json_path, image: nil, smooth: true)
  data = JSON.parse(File.read(json_path))
  image_path = image || File.expand_path(data.dig("meta", "image") || "", File.dirname(json_path))
  raise LoadError, "TextureAtlas: image not found at #{image_path}" unless File.file?(image_path)

  texture = Texture.load(image_path, smooth: smooth)
  regions, durations = _parse_frames(data["frames"])
  new(texture: texture, regions: regions, durations: durations, source: json_path)
end

Instance Method Details

#animation(frame_names, fps: nil, loop: true) ⇒ Object

Build an Animation from a list of frame names. When fps: is nil and Aseprite-style duration data is present, the per-frame durations are used directly — letting artists set timing in Aseprite without re-tuning in code. Pass an explicit fps: to override.



75
76
77
78
79
80
81
82
83
84
85
# File 'lib/sfml/graphics/texture_atlas.rb', line 75

def animation(frame_names, fps: nil, loop: true)
  rects = frame_names.map { |n| region(n) }
  if fps.nil? && frame_names.first && duration(frame_names.first) > 0
    # Use Aseprite's per-frame ms timings — collapse to average
    # fps for the Animation; per-frame durations would need a
    # variable-rate Animation we don't ship yet.
    avg_ms = frame_names.map { |n| duration(n) }.sum / frame_names.size.to_f
    fps = 1000.0 / avg_ms if avg_ms > 0
  end
  Animation.new(self, frames: rects, fps: fps || 12, loop: loop)
end

#duration(name) ⇒ Object

Integer milliseconds per frame, as exported by Aseprite. Returns 0 if the source didn't include durations (TexturePacker etc.).



89
# File 'lib/sfml/graphics/texture_atlas.rb', line 89

def duration(name) = @durations[_normalize(name)] || 0

#frame_names ⇒ Object

Returns the frame names.



54
# File 'lib/sfml/graphics/texture_atlas.rb', line 54

def frame_names = @regions.keys

#region(name) ⇒ SFML::Rect

Returns the pixel rect for frame name.

Returns:

Raises:



58
59
60
61
62
# File 'lib/sfml/graphics/texture_atlas.rb', line 58

def region(name)
  @regions[_normalize(name)] or
    raise LoadError, "TextureAtlas: no frame named #{name.inspect} " \
                     "(have: #{@regions.keys.first(5).inspect}...)"
end

#regions ⇒ Object

The internal frame-name → Rect hash. Useful for filtering:

walk_frames = atlas.regions.select { |k, _| k.start_with?("walk-") }


94
# File 'lib/sfml/graphics/texture_atlas.rb', line 94

def regions = @regions

#sprite(name, **opts) ⇒ Object

Build a fresh Sprite bound to the atlas texture with its texture_rect set to this frame.



66
67
68
# File 'lib/sfml/graphics/texture_atlas.rb', line 66

def sprite(name, **opts)
  Sprite.new(@texture, **opts).tap { |s| s.texture_rect = region(name) }
end

#to_s ⇒ Object Also known as: inspect

String representation for debugging.



97
# File 'lib/sfml/graphics/texture_atlas.rb', line 97

def to_s = "#<TextureAtlas #{@regions.size} frames#{@source ? " from #{File.basename(@source)}" : ""}>"