From 1ce0810cc2fb4f3a6ff7cbdaf51832baa0dac5c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Pluta?= Date: Mon, 21 Jul 2025 13:50:17 +0200 Subject: [PATCH] Refactor animations --- .../base/api/animation/Animated.java | 127 +++++++++++++++++- .../base/api/character/Character.java | 88 ++++++++++++ .../base/lib/character/CharacterDelegate.java | 5 + .../base/util/world/MapObject.java | 3 +- .../world/animation/model/AnimatedSprite.java | 57 ++++++-- .../animation/model/DefaultAnimation.java | 6 - .../manager/CharacterSetManager.java | 5 +- .../manager/DefaultCharacterManager.java | 31 +---- .../manager/DefaultCharacterSetManager.java | 29 ++-- .../world/character/model/CharacterSet.java | 74 ++++++++++ .../character/model/DefaultCharacter.java | 67 +++------ 11 files changed, 386 insertions(+), 106 deletions(-) create mode 100644 engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/model/CharacterSet.java diff --git a/api/src/main/java/com/bartlomiejpluta/base/api/animation/Animated.java b/api/src/main/java/com/bartlomiejpluta/base/api/animation/Animated.java index 57f07dd9..c46bc091 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/api/animation/Animated.java +++ b/api/src/main/java/com/bartlomiejpluta/base/api/animation/Animated.java @@ -2,20 +2,141 @@ package com.bartlomiejpluta.base.api.animation; import com.bartlomiejpluta.base.internal.program.Updatable; +/** + * Represents an object that can be animated by cycling through multiple frames of a texture. + * + *

This interface provides a complete animation system for 2D sprites and other visual objects + * in the game engine. Animated objects automatically cycle through their available frames at a + * specified speed when animation is enabled. + * + *

The animation system works by: + *

+ * + *

Animation can be controlled in real-time through enable/disable methods and speed adjustments. + * The animation automatically resets and pauses when disabled. + * + *

This interface extends {@link Updatable}, meaning animated objects are automatically + * updated each frame by the game engine's update loop. + */ public interface Animated extends Updatable { + + /** + * Returns whether animation is currently enabled for this object. + * + *

When animation is disabled, the object remains on its current frame + * and the internal animation timer is reset to zero. + * + * @return {@code true} if animation is enabled, {@code false} otherwise + */ boolean isAnimationEnabled(); + /** + * Enables or disables animation for this object. + * + *

When animation is disabled: + *

+ * + *

When animation is enabled, the object resumes cycling through frames + * at the configured animation speed. + * + * @param enabled {@code true} to enable animation, {@code false} to disable + */ void setAnimationEnabled(boolean enabled); - void enableAnimation(); + /** + * Enables animation for this object. + * + *

This is a convenience method equivalent to calling + * {@code setAnimationEnabled(true)}. + * + * @see #setAnimationEnabled(boolean) + */ + default void enableAnimation() { + setAnimationEnabled(true); + } - void disableAnimation(); + /** + * Disables animation for this object. + * + *

This is a convenience method equivalent to calling + * {@code setAnimationEnabled(false)}. The object will remain on its + * current frame and the animation timer will be reset. + * + * @see #setAnimationEnabled(boolean) + */ + default void disableAnimation() { + setAnimationEnabled(false); + } - void toggleAnimationEnabled(); + /** + * Toggles the animation enabled state. + * + *

If animation is currently enabled, it will be disabled. + * If animation is currently disabled, it will be enabled. + * + * @see #isAnimationEnabled() + * @see #setAnimationEnabled(boolean) + */ + default void toggleAnimationEnabled() { + setAnimationEnabled(!isAnimationEnabled()); + } + /** + * Returns the current animation speed. + * + *

Animation speed determines how quickly the object cycles through its frames. + * Higher values result in faster animation, while lower values create slower animation. + * + *

The speed is typically measured in relation to the game engine's target + * update rate, where a speed of 1.0 represents normal timing. + * If 60PFS (target update rate) is maintained, it should be measured in frames per second. + * + * @return the current animation speed as a positive float value + */ float getAnimationSpeed(); + /** + * Sets the animation speed for this object. + * + *

Animation speed controls how quickly frames are cycled during animation. + * The speed value is typically: + *

+ * + *

The actual frame timing is calculated based on the game engine's target + * update rate and the specified speed multiplier. + * If 60PFS (target update rate) is maintained, it should be measured in frames per second. + * + * @param speed the new animation speed as a positive float value + * @throws IllegalArgumentException if speed is negative or zero + */ void setAnimationSpeed(float speed); + /** + * Manually sets the current animation frame. + * + *

This method allows direct control over which frame is currently displayed, + * bypassing the automatic animation timing. The frame index is automatically + * wrapped if it exceeds the available frame count. + * + * + *

Frame indices are zero-based, and values that exceed the available frame + * count are automatically wrapped using modulo operation. + * + * @param frame the zero-based index of the frame to display + */ void setAnimationFrame(int frame); } diff --git a/api/src/main/java/com/bartlomiejpluta/base/api/character/Character.java b/api/src/main/java/com/bartlomiejpluta/base/api/character/Character.java index f88770e6..dd82628c 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/api/character/Character.java +++ b/api/src/main/java/com/bartlomiejpluta/base/api/character/Character.java @@ -8,15 +8,103 @@ import com.bartlomiejpluta.base.api.move.Movement; import java.util.concurrent.CompletableFuture; +/** + * Represents a game world object that can face four cardinal directions and uses + * a special texture format called a character set (charset). + * + *

A character set is a sprite sheet organized in a 4-row, multi-column format where: + *

+ * + *

Each column represents a different animation frame or object variant. For animated + * characters, columns contain sequential animation frames. For static objects (like boxes), + * each column may represent different object types or states (e.g., normal box, colored box, + * damaged box). + * + *

This interface combines movement capabilities ({@link Movable}), animation features + * ({@link Animated}), and entity properties ({@link Entity}) to create a complete + * character system for 2D games. + */ public interface Character extends Movable, Animated, Entity { + /** + * Initiates movement in the specified direction. + * + *

This method handles the character's movement mechanics and automatically + * updates the facing direction to match the movement direction. + * + * @param direction the direction to move towards + * @return a {@link Movement} object representing the movement operation + * @throws IllegalArgumentException if direction is null + */ Movement move(Direction direction); + /** + * Returns the current facing direction of the character. + * + *

The facing direction determines which row of the character set + * is used for rendering and animation. + * + * @return the current facing direction + */ Direction getFaceDirection(); + /** + * Sets the default sprite column to use from the character set. + * + *

This method is particularly useful for character sets that contain + * multiple object variants in different columns. For example, if a character + * set contains different box types in each column, this method selects + * which box type to display. + * + *

The column index is zero-based and must be within the bounds of the + * current character set. + * + * @param column the zero-based column index to use as default + * @throws IndexOutOfBoundsException if the column index is invalid + */ + void setDefaultSpriteColumn(int column); + + /** + * Changes the character's facing direction without initiating movement. + * + *

This method updates which row of the character set is used for rendering. + * It can be used to create custom animations by cycling through different + * facing directions, or to orient the character without moving. + * + * @param direction the new facing direction + * @throws IllegalArgumentException if direction is null + */ void setFaceDirection(Direction direction); + /** + * Changes the character set (sprite sheet) used by this character. + * + *

This allows dynamic switching between different visual representations + * of the character, such as different costumes, character states, or + * completely different character types. + * + * @param characterSetUid the unique identifier of the new character set + * @throws IllegalArgumentException if characterSetUid is null or empty + */ void changeCharacterSet(String characterSetUid); + /** + * Performs an instant animation using all frames from the current facing direction. + * + *

This method executes an animation sequence by cycling through all columns + * of the row corresponding to the character's current facing direction. For example, + * if the character is facing UP, the animation will use all frames from the fourth + * row of the character set. + * + *

The animation runs asynchronously and the returned {@link CompletableFuture} + * completes when the animation finishes. + * + * @return a {@link CompletableFuture} that completes when the animation finishes + */ CompletableFuture performInstantAnimation(); } diff --git a/api/src/main/java/com/bartlomiejpluta/base/lib/character/CharacterDelegate.java b/api/src/main/java/com/bartlomiejpluta/base/lib/character/CharacterDelegate.java index 0def00c3..5479ce34 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/lib/character/CharacterDelegate.java +++ b/api/src/main/java/com/bartlomiejpluta/base/lib/character/CharacterDelegate.java @@ -293,6 +293,11 @@ public abstract class CharacterDelegate implements Character { character.setZIndex(zIndex); } + @Override + public void setDefaultSpriteColumn(int column) { + character.setDefaultSpriteColumn(column); + } + @Override public void handleEvent(E event) { character.handleEvent(event); diff --git a/api/src/main/java/com/bartlomiejpluta/base/util/world/MapObject.java b/api/src/main/java/com/bartlomiejpluta/base/util/world/MapObject.java index e5611ac6..91db20d9 100644 --- a/api/src/main/java/com/bartlomiejpluta/base/util/world/MapObject.java +++ b/api/src/main/java/com/bartlomiejpluta/base/util/world/MapObject.java @@ -42,7 +42,8 @@ public abstract class MapObject extends CharacterDelegate { this.frame = frame; setBlocking(true); disableAnimation(); - setAnimationFrame(frame); + setDefaultSpriteColumn(frame); + setFaceDirection(DOWN); pathExecutor.setRepeat(1); initPath(); diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/animation/model/AnimatedSprite.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/animation/model/AnimatedSprite.java index e28eb289..4f2dea93 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/animation/model/AnimatedSprite.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/animation/model/AnimatedSprite.java @@ -6,7 +6,6 @@ import com.bartlomiejpluta.base.engine.core.gl.object.material.Material; import com.bartlomiejpluta.base.engine.world.object.Sprite; import com.bartlomiejpluta.base.util.math.MathUtil; import lombok.EqualsAndHashCode; -import org.joml.Vector2fc; @EqualsAndHashCode(callSuper = true) public abstract class AnimatedSprite extends Sprite implements Animated { @@ -22,9 +21,51 @@ public abstract class AnimatedSprite extends Sprite implements Animated { protected abstract boolean shouldAnimate(); - protected abstract Vector2fc[] getSpriteAnimationFramesPositions(); + /** + * Allows subclasses to restrict animation to a specific subset of frames. + * + *

By default, this method returns {@code null}, which means all frames in the texture + * are available for animation. In this case, the frame IDs used by {@link #setAnimationFrame(int)} + * correspond directly to the global frame indices in the texture, as shown below: + * + *

+    * +----+----+----+----+
+    * | 00 | 01 | 02 | 03 |
+    * +----+----+----+----+
+    * | 04 | 05 | 06 | 07 |
+    * +----+----+----+----+
+    * | 08 | 09 | 10 | 11 |
+    * +----+----+----+----+
+    * | 12 | 13 | 14 | 15 |
+    * +----+----+----+----+
+    * 
+ * + *

However, if this method returns a specific array of frame indices (e.g., {@code {8, 9, 10, 11, 13, 15}}), + * only those frames will be used for animation. The frame IDs are then remapped, where index 0 + * corresponds to the first frame in the subset, index 1 to the second, and so on: + * + *

+    * +----+----+----+----+
+    * |    |    |    |    |
+    * +----+----+----+----+
+    * |    |    |    |    |
+    * +----+----+----+----+
+    * | 00 | 01 | 02 | 03 |
+    * +----+----+----+----+
+    * |    | 04 |    | 05 |
+    * +----+----+----+----+
+    * 
+ * + *

Important: When this method returns {@code null}, calling + * {@link #setAnimationFrame(int)} has the same effect as calling {@code setFrame(int)} + * directly, since the frame IDs map directly to the global texture frame indices. + * When a subset is defined, {@link #setAnimationFrame(int)} uses local indices + * within that subset. + * + * @return an array of global frame IDs to use for animation, or {@code null} to use all frames + */ - protected int[] getAvailableFrames() { + protected int[] getAvailableFramesSubset() { return null; } @@ -40,7 +81,7 @@ public abstract class AnimatedSprite extends Sprite implements Animated { @Override public void setAnimationFrame(int frame) { - var availableFrames = getAvailableFrames(); + var availableFrames = getAvailableFramesSubset(); if (availableFrames == null) { setFrame(frame % getTextureCoordinates().length); @@ -55,11 +96,9 @@ public abstract class AnimatedSprite extends Sprite implements Animated { if (shouldAnimate()) { time += dt * 1000; setAnimationFrame(time / intervalInMilliseconds * intervalInMilliseconds); -// var maxFrames = getTextureCoordinates().length; -// currentAnimationFrame = ((time % (maxFrames * intervalInMilliseconds)) / intervalInMilliseconds); -// setSprite(currentAnimationFrame); - } else { - time = 0; + return; } + + time = 0; } } diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/animation/model/DefaultAnimation.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/animation/model/DefaultAnimation.java index 3f434866..6fcf7ffc 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/animation/model/DefaultAnimation.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/animation/model/DefaultAnimation.java @@ -6,7 +6,6 @@ import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer; import com.bartlomiejpluta.base.api.move.AnimationMovement; import com.bartlomiejpluta.base.api.move.Direction; import com.bartlomiejpluta.base.api.move.Movement; -import com.bartlomiejpluta.base.engine.core.gl.object.material.Material; import com.bartlomiejpluta.base.engine.core.gl.object.texture.Texture; import com.bartlomiejpluta.base.engine.world.movement.MovableSprite; import com.bartlomiejpluta.base.util.path.Path; @@ -90,11 +89,6 @@ public class DefaultAnimation extends MovableSprite implements Animation { return enabled; } - @Override - protected Vector2fc[] getSpriteAnimationFramesPositions() { - return frames; - } - @Override protected void setDefaultAnimationFrame() { // do nothing diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/manager/CharacterSetManager.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/manager/CharacterSetManager.java index 5f9f75f1..9f273271 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/manager/CharacterSetManager.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/manager/CharacterSetManager.java @@ -1,9 +1,8 @@ package com.bartlomiejpluta.base.engine.world.character.manager; import com.bartlomiejpluta.base.engine.common.manager.AssetManager; -import com.bartlomiejpluta.base.engine.core.gl.object.material.Material; -import com.bartlomiejpluta.base.engine.core.gl.object.texture.Texture; import com.bartlomiejpluta.base.engine.world.character.asset.CharacterSetAsset; +import com.bartlomiejpluta.base.engine.world.character.model.CharacterSet; -public interface CharacterSetManager extends AssetManager { +public interface CharacterSetManager extends AssetManager { } diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/manager/DefaultCharacterManager.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/manager/DefaultCharacterManager.java index 79f88d61..313aed2e 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/manager/DefaultCharacterManager.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/manager/DefaultCharacterManager.java @@ -1,44 +1,21 @@ package com.bartlomiejpluta.base.engine.world.character.manager; import com.bartlomiejpluta.base.api.character.Character; -import com.bartlomiejpluta.base.api.move.Direction; import com.bartlomiejpluta.base.engine.util.mesh.MeshManager; import com.bartlomiejpluta.base.engine.world.character.config.CharacterSpriteConfiguration; import com.bartlomiejpluta.base.engine.world.character.model.DefaultCharacter; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.joml.Vector2f; -import org.joml.Vector2fc; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import java.util.Map; -import java.util.Map.Entry; - -import static java.util.stream.Collectors.toUnmodifiableMap; - @Slf4j @Component +@RequiredArgsConstructor(onConstructor_ = @__(@Autowired)) public class DefaultCharacterManager implements CharacterManager { private final MeshManager meshManager; private final CharacterSetManager characterSetManager; - - private final int defaultSpriteColumn; - private final Map spriteDirectionRows; - private final Map spriteDefaultRows; - - @Autowired - public DefaultCharacterManager(MeshManager meshManager, CharacterSetManager characterSetManager, CharacterSpriteConfiguration configuration) { - this.meshManager = meshManager; - this.characterSetManager = characterSetManager; - - this.spriteDirectionRows = configuration.getSpriteDirectionRows(); - - defaultSpriteColumn = configuration.getDefaultSpriteColumn(); - this.spriteDefaultRows = spriteDirectionRows - .entrySet() - .stream() - .collect(toUnmodifiableMap(Entry::getKey, entry -> new Vector2f(defaultSpriteColumn, entry.getValue()))); - } + private final CharacterSpriteConfiguration configuration; @Override public void init() { @@ -47,7 +24,7 @@ public class DefaultCharacterManager implements CharacterManager { @Override public Character createCharacter(String characterSetUid) { - return new DefaultCharacter(characterSetManager, defaultSpriteColumn, spriteDirectionRows, spriteDefaultRows, characterSetUid); + return new DefaultCharacter(characterSetManager, characterSetUid, configuration.getDefaultSpriteColumn()); } @Override diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/manager/DefaultCharacterSetManager.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/manager/DefaultCharacterSetManager.java index 29400619..42b2331e 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/manager/DefaultCharacterSetManager.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/manager/DefaultCharacterSetManager.java @@ -1,11 +1,11 @@ package com.bartlomiejpluta.base.engine.world.character.manager; -import com.bartlomiejpluta.base.engine.core.gl.object.material.Material; -import com.bartlomiejpluta.base.engine.core.gl.object.texture.Texture; import com.bartlomiejpluta.base.engine.core.gl.object.texture.TextureManager; import com.bartlomiejpluta.base.engine.error.AppException; import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration; import com.bartlomiejpluta.base.engine.world.character.asset.CharacterSetAsset; +import com.bartlomiejpluta.base.engine.world.character.config.CharacterSpriteConfiguration; +import com.bartlomiejpluta.base.engine.world.character.model.CharacterSet; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -20,6 +20,8 @@ import java.util.Map; public class DefaultCharacterSetManager implements CharacterSetManager { private final TextureManager textureManager; private final Map assets = new HashMap<>(); + private final Map charSets = new HashMap<>(); + private final CharacterSpriteConfiguration charsetConfiguration; private final ProjectConfiguration configuration; @Override @@ -34,15 +36,24 @@ public class DefaultCharacterSetManager implements CharacterSetManager { } @Override - public Texture loadObject(String uid) { - var asset = assets.get(uid); + public CharacterSet loadObject(String uid) { + var charSet = charSets.get(uid); - if (asset == null) { - throw new AppException("The character set asset with UID: [%s] does not exist", uid); + if (charSet == null) { + var asset = assets.get(uid); + + if (asset == null) { + throw new AppException("The character set asset with UID: [%s] does not exist", uid); + } + + var source = configuration.projectFile("charsets", asset.getSource()); + + var texture = textureManager.loadTexture(source, asset.getRows(), asset.getColumns()); + charSet = CharacterSet.from(texture, charsetConfiguration.getSpriteDirectionRows()); + log.info("Loading character set from assets to cache under the key: [{}]", uid); + charSets.put(uid, charSet); } - var source = configuration.projectFile("charsets", asset.getSource()); - - return textureManager.loadTexture(source, asset.getRows(), asset.getColumns()); + return charSet; } } diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/model/CharacterSet.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/model/CharacterSet.java new file mode 100644 index 00000000..ca4013a6 --- /dev/null +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/model/CharacterSet.java @@ -0,0 +1,74 @@ +package com.bartlomiejpluta.base.engine.world.character.model; + +import com.bartlomiejpluta.base.api.move.Direction; +import com.bartlomiejpluta.base.engine.core.gl.object.texture.Texture; +import lombok.Getter; +import lombok.NonNull; + +import java.util.Map; +import java.util.function.Function; + +import static com.bartlomiejpluta.base.api.move.Direction.values; +import static java.util.Arrays.stream; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toUnmodifiableMap; +import static java.util.stream.IntStream.range; + +@Getter +public class CharacterSet { + private final Texture texture; + private final Map frames; + + private CharacterSet(@NonNull Texture texture, @NonNull Map charsetRowsByDirections) { + this.texture = texture; + this.frames = stream(values()) + .collect(toUnmodifiableMap(identity(), calculateFrames(charsetRowsByDirections))); + } + + private Function calculateFrames(Map charsetRowsByDirections) { + return d -> framesForRow(charsetRowsByDirections.get(d)); + } + + private int[] framesForRow(int row) { + var cols = texture.getColumns(); + return range(row * cols, (row + 1) * cols).toArray(); + } + + /** + * Creates a new CharacterSet instance from the specified texture and direction-to-row mapping. + * + *

This factory method constructs a CharacterSet by associating each facing direction + * with its corresponding row in the character set texture. The method automatically + * calculates frame indices for each direction based on the texture dimensions and + * row mappings. + * + *

The character set texture should be organized as a grid where: + *

+ * + *

Example mapping: + *

{@code
+    * Map mapping = Map.of(
+    *     Direction.DOWN, 0,   // First row (index 0) for DOWN direction
+    *     Direction.LEFT, 1,   // Second row (index 1) for LEFT direction
+    *     Direction.RIGHT, 2,  // Third row (index 2) for RIGHT direction
+    *     Direction.UP, 3      // Fourth row (index 3) for UP direction
+    * );
+    * CharacterSet charset = CharacterSet.from(texture, mapping);
+    * }
+ * + * @param texture the character set texture containing sprite graphics organized in rows and columns + * @param charsetRowsByDirections a mapping that defines which texture row corresponds to each facing direction, + * where the key is the direction and the value is the zero-based row index + * @return a new CharacterSet instance configured with the specified texture and direction mappings + * @throws NullPointerException if either texture or charsetRowsByDirections is null + * @throws IllegalArgumentException if the charsetRowsByDirections map is empty or contains invalid row indices + * @throws IndexOutOfBoundsException if any row index in the mapping exceeds the texture's row count + */ + public static CharacterSet from(Texture texture, Map charsetRowsByDirections) { + return new CharacterSet(texture, charsetRowsByDirections); + } +} diff --git a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/model/DefaultCharacter.java b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/model/DefaultCharacter.java index 23169b34..d43a21a3 100644 --- a/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/model/DefaultCharacter.java +++ b/engine/src/main/java/com/bartlomiejpluta/base/engine/world/character/model/DefaultCharacter.java @@ -7,8 +7,6 @@ import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer; import com.bartlomiejpluta.base.api.move.CharacterMovement; import com.bartlomiejpluta.base.api.move.Direction; import com.bartlomiejpluta.base.api.move.Movement; -import com.bartlomiejpluta.base.engine.core.gl.object.material.Material; -import com.bartlomiejpluta.base.engine.core.gl.object.texture.Texture; import com.bartlomiejpluta.base.engine.error.AppException; import com.bartlomiejpluta.base.engine.world.character.manager.CharacterSetManager; import com.bartlomiejpluta.base.engine.world.movement.MovableSprite; @@ -21,7 +19,6 @@ import org.joml.Vector2f; import org.joml.Vector2fc; import java.util.LinkedList; -import java.util.Map; import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -30,24 +27,15 @@ import static java.util.Objects.requireNonNull; @EqualsAndHashCode(callSuper = true) public class DefaultCharacter extends MovableSprite implements Character { - private static final Map CHARSET_FRAMES = Map.of( - Direction.DOWN, new int[]{0, 1, 2, 3}, - Direction.LEFT, new int[]{4, 5, 6, 7}, - Direction.RIGHT, new int[]{8, 9, 10, 11}, - Direction.UP, new int[]{12, 13, 14, 15} - ); - - private static final int DEFAULT_CHARSET_FRAME_COLUMN = 0; - - private final int defaultSpriteColumn; private final CharacterSetManager characterSetManager; - private final Map spriteDirectionRows; - private final Map spriteDefaultRows; private final Vector2f characterScale = new Vector2f(1, 1); - private Texture texture; - private Vector2fc characterSetSize; - private final EventHandler eventHandler = new EventHandler(); + private final Queue instantAnimations = new LinkedList<>(); + + @Setter + private int defaultSpriteColumn; + private CharacterSet characterSet; + private Vector2fc characterSetSize; @Getter @Setter @@ -65,22 +53,19 @@ public class DefaultCharacter extends MovableSprite implements Character { private boolean animationEnabled = true; - private final Queue instantAnimations = new LinkedList<>(); - - public DefaultCharacter(CharacterSetManager characterSetManager, int defaultSpriteColumn, @NonNull Map spriteDirectionRows, Map spriteDefaultRows, @NonNull String characterSetUid) { - this(characterSetManager.loadObject(characterSetUid), characterSetManager, defaultSpriteColumn, spriteDirectionRows, spriteDefaultRows); + public DefaultCharacter(CharacterSetManager characterSetManager, @NonNull String characterSetUid, int defaultSpriteColumn) { + this(characterSetManager.loadObject(characterSetUid), characterSetManager, defaultSpriteColumn); } - private DefaultCharacter(@NonNull Texture texture, @NonNull CharacterSetManager characterSetManager, int defaultSpriteColumn, @NonNull Map spriteDirectionRows, @NonNull Map spriteDefaultRows) { - super(texture); + private DefaultCharacter(@NonNull CharacterSet characterSet, @NonNull CharacterSetManager characterSetManager, int defaultSpriteColumn) { + super(characterSet.getTexture()); + this.defaultSpriteColumn = defaultSpriteColumn; this.characterSetManager = characterSetManager; - this.spriteDirectionRows = spriteDirectionRows; this.faceDirection = Direction.DOWN; - this.spriteDefaultRows = spriteDefaultRows; + this.characterSet = characterSet; + this.characterSetSize = characterSet.getTexture().getSpriteSize(); - this.texture = texture; - this.characterSetSize = texture.getSpriteSize(); super.setScale(characterSetSize.x() * characterScale.x, characterSetSize.y() * characterScale.y); setDefaultAnimationFrame(); @@ -117,35 +102,21 @@ public class DefaultCharacter extends MovableSprite implements Character { } @Override - protected Vector2fc[] getSpriteAnimationFramesPositions() { - var row = spriteDirectionRows.get(faceDirection); - var frames = getMaterial().getColumns(); - var array = new Vector2f[frames]; - - for (int column = 0; column < frames; ++column) { - array[column] = new Vector2f(column, row); - } - - return array; - } - - @Override - protected int[] getAvailableFrames() { - return CHARSET_FRAMES.get(faceDirection); + protected int[] getAvailableFramesSubset() { + return characterSet.getFrames().get(faceDirection); } @Override protected void setDefaultAnimationFrame() { -// getMaterial().setSpritePosition(spriteDefaultRows.get(faceDirection)); - setAnimationFrame(CHARSET_FRAMES.get(faceDirection)[DEFAULT_CHARSET_FRAME_COLUMN]); + setAnimationFrame(characterSet.getFrames().get(faceDirection)[defaultSpriteColumn]); } @Override public void changeCharacterSet(String characterSetUid) { - this.texture = characterSetManager.loadObject(characterSetUid); - this.characterSetSize = texture.getSpriteSize(); + this.characterSet = characterSetManager.loadObject(characterSetUid); + this.characterSetSize = characterSet.getTexture().getSpriteSize(); super.setScale(characterSetSize.x() * characterScale.x, characterSetSize.y() * characterScale.y); - setMaterial(texture); + setMaterial(characterSet.getTexture()); } @Override