-1
\$\begingroup\$

I've created a class called DegreeAnimation, which is basically an implementation of my CustomAnimation class, but works with other directions, there are 2 constructors for this class, one where the algorithm sets all the ranges by themselves and assigning textures to them, starting from east and ending at the topmost of southeast, and another one that takes in ranges manually when it's needed, like in mine, as i need to divide the ranges like this and not like this:

enter image description here Even so, since i cant just do 240° - 70°, as it would wrap backwards, so there are 2 ranges for the same texture, also that with the current implementation, there are small gaps inbetween like 60°-61° but that isn't really the problem here as i will fix them sooner or later, the problem here is that, in order to properly render them as i want, there needs to be 3 variables, the textureRotation, and whether it will be flipped or not all inside the function: flipX, flipY, and initial values for the assets namely as, needsFlipX, needsFlipY and initialRotation (the direction the sprite looks at in its texture form), also, in this code i have converted +-180 degree notation to +360 cause that's what atan2 works with. I am also unable to just hardcore those values, as i actually want some degree of rotation feeling in my game, I'm still yet to implement the getter values of initialRotation, needsXFlip and needsYFlip in the CustomAnimationAndRange data class, but please see the code as if those were implemented. A video to show what happens, and i guess you can imagine what I actually intended.

enter image description here

Tileset: https://stealthix.itch.io/rpg-nature-tileset Player: https://pixel-boy.itch.io/ninja-adventure-asset-pack

outside my most probably wrong range sets, you must have noticed that something is very out of the place, and I hereby ask for help on fixing it. Also, one has to absolutely call thattexture.flip(false,false) as it carries on due to referencial implementation of this in java. Also, there are many stuff that I explicitly left out broken as i was going to delete them later after i implemented this, but I couldnt.

class DegreeAnimation {
    /**
     * I literally have no idea why some require flips and some don't
     */
    data class CustomAnimationAndRange( val customAnimation: CustomAnimation,
                                        val range: ClosedFloatingPointRange<Float>,
                                        val initialRotation: Int = 0,
                                        val needsXFlip : Boolean = false,
                                        val needsYFlip : Boolean = false)
    var internalTimer: Float
    get() {
        return animations[0].second.internalTimer
        // will crash if the index-0 of the animationsTemp is null
    }
    set(value) {
        animations[0].second.internalTimer = value
    }

    var currentFrame: Int
        get()  = animations[0].second.currentFrame
        set(value) {
        animations[0].second.currentFrame = value
        }
    val frameAmountCache: Int
        get() = animations[0].second.frameAmountCache

    val timingArray: Array<Float>?
        get() = animations[0].second.timingArray

    val isOneTexture: Boolean
        get() = animations[0].second.isOneTexture

    val atLastTexture : Boolean
        get() = animations[0].second.atLastTexture

    var isLooping : Boolean
        get() = animations[0].second.isLooping
        set(value)
        {
            animations[0].second.isLooping = value
        }

    internal val cachedTimePerFrame: Array<Float>?
        get() = animations[0].second.cachedTimePerFrame

    var paused : Boolean
    get() = animations[0].second.paused
    set(value) {
        animations[0].second.paused = value
    }

    var animations : Array<Triple<ClosedFloatingPointRange<Float>, CustomAnimation, Int>>
    private var delegate : Sprite? = null
    val rotation : Float?
        get() {
            if(delegate == null) return null
            if(delegate!!.rotation < 0)
                return delegate?.rotation!! + 360
            else
                return delegate?.rotation
    }

    /**
     * if this isn't updated in getTexture for some reason there will be huge discrepancies
     */
    var textureRotation : Float = 0f
        get(){
            return field
        }
        set(value) {
        field = value
        }
    /**
     * The first element of animation is taken as the ultimate example for al land will be used for so
     * The arguments go from counter-clockwise, starting at east (0°) ending at topmost of south-east (360°)
     */
    fun update(delta: Float)
    {
        animations[0].second.update(delta)
    }
    var usesAutoDegree = false
    /**
     * if flipOver180Textures is set to true, ABSOLUTELY call flip(false, false) AFTER **USING** the texture
     */
    fun getTexture(rotation : Float? = this.rotation, flipOver180Textures: Boolean = false) : TextureRegion
    {
        rotation!!
        for(i in animations.indices)
        {
            if(animations[i].first.contains(rotation))
            {
                var initial = animations[i].third
                textureRotation = initial + rotation - animations[i].first.start
                var outputNeedsFlipX : Boolean = ?
                var outputNeedsFlipY : Boolean = ?
                if(rotation > 180 && flipOver180Textures)
                    return animations[i].second.
                    getKeyFrame(currentFrame).apply {    //i havent integrated needsXFlip to the animation[] yet
                        this.flip(false.xor(animations[i].needsXFlip), true.xor(animations[i].needsYFlip)) } // please act as if 
                else                                                                            // it has been
                    return animations[i].second.getKeyFrame(currentFrame)
            }
        }
        for(i in animations.indices) // this tries to fill in the gaps after automatic assignation or something else 
        {
            if(i != animations.size-1)
            {
                if(animations[i].first.endInclusive.rangeTo(animations[i+1].first.start).contains(rotation))
                {
                    if(rotation > 180 && flipOver180Textures)
                        return animations[i].second.getKeyFrame(currentFrame).apply {  flip(true, true) }
                    else
                        return animations[i].second.getKeyFrame(currentFrame)
                    // todo: implement rounding for inbetween i.end, i+1.start and return it like so
                }
            }

        }
        return Globals.ItemTextures["null"]!! // if none found
    }
    constructor(sprite: Sprite? = null, vararg animation: CustomAnimation)
    {
        for(i in 1 until animation.size)
        {
            if(!animation[i].timingArray.contentEquals(animation[0].timingArray!!))
            {
                Console.log("the length of animation aren't the same, or they aren't equal to the animation[0]")
            }
        }
        var animationsTemp :  Array<Triple<ClosedFloatingPointRange<Float>, CustomAnimation, Int>?> = Array(animation.size){null}
        var partSize = 360f/animationsTemp.size
        for(i in animationsTemp.indices)
        {
            if(i == 0)
            {
                animationsTemp[0] = Triple(0f.rangeTo((i+1)*partSize), animation[i], 0)
            }
            else
            {

                animationsTemp[i] = Triple(((i*partSize)+1).rangeTo((i+1)*partSize), animation[i], 0)
            }
        }
        var tempIterator = animationsTemp.iterator()
        animations = Array(animationsTemp.size){tempIterator.next()!!}
        delegate = sprite
        usesAutoDegree = true
    }
    constructor(sprite: Sprite?,
                vararg customAnimationAndRange: CustomAnimationAndRange) 
    {
        usesAutoDegree = false
        for(i in 1 until customAnimationAndRange.size)
        {
            if(!customAnimationAndRange[i].customAnimation.timingArray.
                contentEquals(customAnimationAndRange[0].customAnimation.timingArray!!))
            {
                Console.log("the length of animation aren't the same, or they aren't equal to the animation[0]")
            }
        }
        var animationsTemp :  Array<Triple<ClosedFloatingPointRange<Float>, CustomAnimation, Int>?>
        = Array(customAnimationAndRange.size){null}
        for(i in animationsTemp.indices)
        {
            animationsTemp[i] = Triple(customAnimationAndRange[i].range,
                customAnimationAndRange[i].customAnimation,
                customAnimationAndRange[i].initialRotation)
        }
        var tempIterator = animationsTemp.iterator() // this part is not required, but I want a non-nullable animation
        animations = Array(animationsTemp.size){tempIterator.next()!!} // collection, also filterNotNull might also work
        delegate = sprite                            // but I mean we are sure that they are non-nullable, so we use !!
    }                                                // so fearing filterNotNull slipping some stuff is useless
}

this code assumes that the properties of all CustomAnimations are the same other than textures so it takes [0] as the ultimate example, if requested, I will post the details of my CustomAnimation class, I didnt post it at the first place because this wall of text is littered enough, also that I think that the naming convention of that class is well enough, the most imporant of them all is the GetTexture() function, and this is what my main class looks like if I only took things related to this:

        player = Entity() // Entity : Sprite
        degreeAnimation = DegreeAnimation(player,
            DegreeAnimation.CustomAnimationAndRange(
                Globals.Animations["player.walkRight"]?.invoke()!!,
                0f.rangeTo(60f)),
            DegreeAnimation.CustomAnimationAndRange(
                Globals.Animations["player.walkRight"]?.invoke()!!,
                300f.rangeTo(360f)),
            DegreeAnimation.CustomAnimationAndRange(
                Globals.Animations["player.walkUp"]?.invoke()!!,
                60f.rangeTo(120f), 90, needsXFlip = false, true),
            DegreeAnimation.CustomAnimationAndRange(
            Globals.Animations["player.walkLeft"]?.invoke()!!,
                120f.rangeTo(240f), 300),
            DegreeAnimation.CustomAnimationAndRange(
            Globals.Animations["player.walkDown"]?.invoke()!!,
            200f.rangeTo(300f), 270)
        )

    render(delta: Float)
    {
        var aaa = degreeAnimation.apply { update(delta) }
        mainBatch.draw(aaa.getTexture()  , player.x, player.y,
        player.getWorldOriginX * player.readOnlyScaling.x , player.getWorldOriginY * player.readOnlyScaling.y,
            player.getWorldSizeX, player.getWorldSizeY, player.scaleX,
            player.scaleY, aaa.textureRotation)
        aaa.getTexture().flip(false,false)
        player.setOriginCenter()
        player.rotation = degreesToMouse(player)
    }
    fun degreesToMouse(arg: Entity) : Float
    {
        var radians = atan2(Gdx.input.y.toDouble() - player.screenPosition.y + player.originY * player.readOnlyScaling.y /*+ player.originY * player.readOnlyScaling.y*/,
            Gdx.input.x.toDouble() - player.screenPosition.x + player.originX * player.readOnlyScaling.x /*+ player.originX * player.readOnlyScaling.x*/)
        return  -(radians * (180 / Math.PI)).toFloat()
    }

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

See Asking and, because I know you've been linked it previously, The Good Asker. No-one is going to fix all of your code for free regardless of where you ask. It would be beneficial for us all to create a new project with the bare minimum of what you're trying to achieve.

It's not very clear what you're asking, but here's a basic example of changing a sprite and flipping it, similar to Pokémon on the Game Boy. It does what your diagram says explicitly not to do, but if it only showed the up/down frame when facing exactly up or down (I think that's what you want but it doesn't really make sense if not limited to a set number of directions) we wouldn't see it very clearly.

SpriteBatch batch;
Texture up, down, right;
Sprite player;

@Override
public void create () {
  batch = new SpriteBatch();
  
  up = new Texture("up.png");
  down = new Texture("down.png");
  right = new Texture("right.png");

  player = new Sprite(right);
  player.setOriginCenter();
}

@Override
public void render () {
  ScreenUtils.clear(Color.GRAY);
  batch.begin();
  
  // Set the sprite's angle
  float angle = Gdx.graphics.getFrameId() % 360;
  player.setRotation(angle);

  // Flip horizontally if facing left
  player.setFlip(angle >= 90 && angle <= 270, false);

  // Set appropriate texture for side view, up and down
  if (angle >= 45 && angle <= 135) {
    player.setTexture(up);
  } else if (angle >= 225 && angle <= 315) {
    player.setTexture(down);
  } else {
    player.setTexture(right);
  }

  player.draw(batch);
  batch.end();
}
up.png down.png right.png

Example animation

\$\endgroup\$
3
  • \$\begingroup\$ I've tested your code and it seems as such that this is exactly what my code already does, as seen here: i.sstatic.net/3URxR.gif \$\endgroup\$ Commented Dec 14, 2021 at 8:22
  • \$\begingroup\$ @logic What is the problem, just your codebase in general? Displaying the player at any angle when this was only designed for four directions, as seen in the asset pack's demo, will always look a bit strange. \$\endgroup\$
    – spacer GIF
    Commented Dec 14, 2021 at 13:22
  • \$\begingroup\$ i want to fix that strangeness, but have no idea how to, if i set flip(true, true) with my current code, with exception to misplacements of some rangs, it all works except for the up direction, video: i.sstatic.net/uN5yy.gif \$\endgroup\$ Commented Dec 14, 2021 at 14:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .