diff --git a/AndEngine/src/org/anddev/andengine/entity/Entity.java b/AndEngine/src/org/anddev/andengine/entity/Entity.java index 6b7eb87d3..95bb17828 100644 --- a/AndEngine/src/org/anddev/andengine/entity/Entity.java +++ b/AndEngine/src/org/anddev/andengine/entity/Entity.java @@ -88,14 +88,17 @@ public void call(final IEntity pEntity) { protected float mScaleCenterX = 0; protected float mScaleCenterY = 0; - private boolean mLocalToParentTransformationDirty = true; - private boolean mParentToLocalTransformationDirty = true; + // BEGIN osu!droid modified: Make these fields protected + /*private*/ protected boolean mLocalToParentTransformationDirty = true; + /*private*/ protected boolean mParentToLocalTransformationDirty = true; - private Transformation mLocalToParentTransformation; - private Transformation mParentToLocalTransformation; + /*private*/ protected Transformation mLocalToParentTransformation; + /*private*/ protected Transformation mParentToLocalTransformation; + + /*private*/ protected Transformation mLocalToSceneTransformation; + /*private*/ protected Transformation mSceneToLocalTransformation; + // END osu!droid modified - private Transformation mLocalToSceneTransformation; - private Transformation mSceneToLocalTransformation; private Object mUserData; diff --git a/AndEngine/src/org/anddev/andengine/opengl/vertex/TextVertexBuffer.java b/AndEngine/src/org/anddev/andengine/opengl/vertex/TextVertexBuffer.java index d9fc9e8ac..57c3d76c1 100644 --- a/AndEngine/src/org/anddev/andengine/opengl/vertex/TextVertexBuffer.java +++ b/AndEngine/src/org/anddev/andengine/opengl/vertex/TextVertexBuffer.java @@ -48,7 +48,13 @@ public TextVertexBuffer(final int pCharacterCount, final HorizontalAlign pHorizo // Methods // =========================================================== + // BEGIN osu!droid modified - Pass horizontal alignment through to update method. public synchronized void update(final Font font, final int pMaximumLineWidth, final int[] pWidths, final String[] pLines) { + this.update(font, pMaximumLineWidth, pWidths, pLines, this.mHorizontalAlign); + } + // END osu!droid modified + + public synchronized void update(final Font font, final int pMaximumLineWidth, final int[] pWidths, final String[] pLines, HorizontalAlign pHorizontalAlign) { final FloatBuffer floatBuffer = this.mFloatBuffer; int i = 0; @@ -59,7 +65,7 @@ public synchronized void update(final Font font, final int pMaximumLineWidth, fi final String line = pLines[lineIndex]; float lineX; - switch(this.mHorizontalAlign) { + switch(pHorizontalAlign) { case RIGHT: lineX = pMaximumLineWidth - pWidths[lineIndex]; break; diff --git a/src/com/edlplan/osu/support/slider/SliderBody.java b/src/com/edlplan/osu/support/slider/SliderBody.java index f1a05ca5a..a750eb74c 100644 --- a/src/com/edlplan/osu/support/slider/SliderBody.java +++ b/src/com/edlplan/osu/support/slider/SliderBody.java @@ -2,6 +2,7 @@ import com.edlplan.andengine.TriangleBuilder; import com.edlplan.framework.math.line.LinePath; +import com.reco1l.andengine.DepthInfo; import com.reco1l.andengine.shape.TriangleMesh; import com.reco1l.andengine.container.Container; import com.rian.osu.math.Vector2; @@ -38,18 +39,17 @@ public SliderBody(boolean allowHint) { if (allowHint) { hint = new TriangleMesh(); hint.setVisible(false); - hint.setTestWithDepthBuffer(true); - hint.setClearDepthBufferBeforeDraw(true); + hint.setDepthInfo(DepthInfo.Clear); } else { hint = null; } border = new TriangleMesh(); - border.setTestWithDepthBuffer(true); + border.setDepthInfo(DepthInfo.Default); attachChild(border, 0); background = new TriangleMesh(); - background.setTestWithDepthBuffer(true); + background.setDepthInfo(DepthInfo.Default); attachChild(background, 0); if (hint != null) { @@ -90,9 +90,9 @@ public void setBackgroundColor(float r, float g, float b, float a) { public void setHintVisible(boolean visible) { if (hint != null) { hint.setVisible(visible); - background.setClearDepthBufferBeforeDraw(!visible); + background.setDepthInfo(visible ? DepthInfo.Default : DepthInfo.Clear); } else { - background.setClearDepthBufferBeforeDraw(true); + background.setDepthInfo(DepthInfo.Clear); } } diff --git a/src/com/reco1l/andengine/Anchor.kt b/src/com/reco1l/andengine/Anchor.kt index e3eaa05d8..43c3e63e3 100644 --- a/src/com/reco1l/andengine/Anchor.kt +++ b/src/com/reco1l/andengine/Anchor.kt @@ -1,26 +1,34 @@ package com.reco1l.andengine -/** - * The anchor points in the range of [0, 1]. - */ -enum class Anchor(val factorX: Float, val factorY: Float) { +import com.reco1l.framework.math.Vec2 - TopLeft(0f, 0f), +object Anchor { - TopCenter(0.5f, 0f), + @JvmField + val TopLeft = Vec2(0f, 0f) - TopRight(1f, 0f), + @JvmField + val TopCenter = Vec2(0.5f, 0f) - CenterLeft(0f, 0.5f), + @JvmField + val TopRight = Vec2(1f, 0f) - Center(0.5f, 0.5f), + @JvmField + val CenterLeft = Vec2(0f, 0.5f) - CenterRight(1f, 0.5f), + @JvmField + val Center = Vec2(0.5f, 0.5f) - BottomLeft(0f, 1f), + @JvmField + val CenterRight = Vec2(1f, 0.5f) - BottomCenter(0.5f, 1f), + @JvmField + val BottomLeft = Vec2(0f, 1f) - BottomRight(1f, 1f) + @JvmField + val BottomCenter = Vec2(0.5f, 1f) + + @JvmField + val BottomRight = Vec2(1f, 1f) } \ No newline at end of file diff --git a/src/com/reco1l/andengine/Axes.kt b/src/com/reco1l/andengine/Axes.kt index 9d9ca0f79..4b9b82bf4 100644 --- a/src/com/reco1l/andengine/Axes.kt +++ b/src/com/reco1l/andengine/Axes.kt @@ -23,5 +23,20 @@ enum class Axes { /** * None of the axes. */ - None + None; + + + /** + * Whether this axis is [Y] or [Both]. + */ + val isVertical: Boolean + get() = this == Y || this == Both + + /** + * Whether this axis is [X] or [Both]. + */ + val isHorizontal: Boolean + get() = this == X || this == Both + + } \ No newline at end of file diff --git a/src/com/reco1l/andengine/BlendInfo.kt b/src/com/reco1l/andengine/BlendInfo.kt new file mode 100644 index 000000000..e3776b639 --- /dev/null +++ b/src/com/reco1l/andengine/BlendInfo.kt @@ -0,0 +1,59 @@ +package com.reco1l.andengine + +import javax.microedition.khronos.opengles.GL10 + +data class BlendInfo( + + /** + * The blending function to use. + */ + var function: BlendingFunction, + + /** + * Whether to mask the red channel. + */ + var redMask: Boolean = true, + + /** + * Whether to mask the green channel. + */ + var greenMask: Boolean = true, + + /** + * Whether to mask the blue channel. + */ + var blueMask: Boolean = true, + + /** + * Whether to mask the alpha channel. + */ + var alphaMask: Boolean = true, + + /** + * Whether to clear the color buffer. + */ + var clear: Boolean = false + +) { + + fun apply(gl: GL10) { + + gl.glColorMask(redMask, greenMask, blueMask, alphaMask) + + if (function != BlendingFunction.Inherit) { + gl.glBlendFunc(function.source, function.destination) + } + + if (clear) { + gl.glClear(GL10.GL_COLOR_BUFFER_BIT) + } + } + + + companion object { + + val Inherit = BlendInfo(BlendingFunction.Inherit) + + } + +} \ No newline at end of file diff --git a/src/com/reco1l/andengine/Camera.kt b/src/com/reco1l/andengine/Camera.kt new file mode 100644 index 000000000..d43065c3d --- /dev/null +++ b/src/com/reco1l/andengine/Camera.kt @@ -0,0 +1,31 @@ +package com.reco1l.andengine + +import org.anddev.andengine.engine.camera.Camera +import org.anddev.andengine.util.MathUtils + + +fun Camera.getScreenSpaceCoordinates(coordinates: FloatArray): FloatArray { + + var (x, y) = coordinates + + val relativeX = (x - minX) / (maxX - minX) + val relativeY = 1f - (y - minY) / (maxY - minY) + + x = relativeX * surfaceWidth + y = relativeY * surfaceHeight + + if (rotation != 0f) { + + if (rotation == 180f) { + coordinates[0] = surfaceHeight - y + coordinates[1] = surfaceWidth - x + } else { + MathUtils.revertRotateAroundCenter(coordinates, -rotation, surfaceWidth.shr(1).toFloat(), surfaceHeight.shr(1).toFloat()) + } + } else { + coordinates[0] = x + coordinates[1] = y + } + + return coordinates +} diff --git a/src/com/reco1l/andengine/DepthInfo.kt b/src/com/reco1l/andengine/DepthInfo.kt new file mode 100644 index 000000000..697bf103d --- /dev/null +++ b/src/com/reco1l/andengine/DepthInfo.kt @@ -0,0 +1,71 @@ +package com.reco1l.andengine + +import org.anddev.andengine.opengl.util.GLHelper +import javax.microedition.khronos.opengles.GL10 + +/** + * Information about how to behave with the depth buffer. + */ +data class DepthInfo( + + /** + * The depth function to use. + */ + val function: DepthFunction, + + /** + * Whether to write to the depth buffer. + */ + val mask: Boolean, + + /** + * Whether to clear the depth buffer. + */ + val clear: Boolean + +) { + + + fun apply(gl: GL10) { + gl.glDepthFunc(function.glType) + gl.glDepthMask(mask) + + if (clear) { + gl.glClear(GL10.GL_DEPTH_BUFFER_BIT) + } + + GLHelper.enableDepthTest(gl) + } + + + companion object { + + @JvmField + val Clear = DepthInfo( + function = DepthFunction.Less, + mask = true, + clear = true + ) + + @JvmField + val Default = DepthInfo( + function = DepthFunction.Less, + mask = true, + clear = false + ) + + } + +} + + +enum class DepthFunction(val glType: Int) { + Never(GL10.GL_NEVER), + Less(GL10.GL_LESS), + Equal(GL10.GL_EQUAL), + LessOrEqual(GL10.GL_LEQUAL), + Greater(GL10.GL_GREATER), + NotEqual(GL10.GL_NOTEQUAL), + GreaterOrEqual(GL10.GL_GEQUAL), + Always(GL10.GL_ALWAYS) +} \ No newline at end of file diff --git a/src/com/reco1l/andengine/Entities.kt b/src/com/reco1l/andengine/Entities.kt new file mode 100644 index 000000000..00a3f653e --- /dev/null +++ b/src/com/reco1l/andengine/Entities.kt @@ -0,0 +1,123 @@ +package com.reco1l.andengine + +import com.reco1l.framework.math.Vec2 +import com.reco1l.framework.math.Vec4 +import org.anddev.andengine.entity.IEntity +import org.anddev.andengine.entity.scene.CameraScene +import org.anddev.andengine.entity.shape.IShape + + +fun IEntity?.getPadding() = when (this) { + is ExtendedEntity -> padding + else -> Vec4.Zero +} + +fun IEntity?.getPaddedWidth() = when (this) { + is ExtendedEntity -> drawWidth - padding.horizontal + is CameraScene -> camera.widthRaw + is IShape -> width + else -> 0f +} + +fun IEntity?.getPaddedHeight() = when (this) { + is ExtendedEntity -> drawHeight - padding.vertical + is CameraScene -> camera.heightRaw + is IShape -> height + else -> 0f +} + + +/** + * The size of the entity. + * + * When using the getter this will return the maximum value between the width and height or the same. + * When using the setter this will set the width and height to the same value. + */ +var ExtendedEntity.size + get() = Vec2(width, height) + set(value) { + width = value.x + height = value.y + } + + +/** + * The total offset applied to the X axis. + */ +val ExtendedEntity.totalOffsetX + get() = originOffsetX + anchorOffsetX + translationX + +/** + * The total offset applied to the Y axis. + */ +val ExtendedEntity.totalOffsetY + get() = originOffsetY + anchorOffsetY + translationY + +/** + * The offset applied to the X axis according to the anchor factor. + */ +val ExtendedEntity.anchorOffsetX: Float + get() = parent.getPaddedWidth() * anchor.x + +/** + * The offset applied to the Y axis according to the anchor factor. + */ +val ExtendedEntity.anchorOffsetY: Float + get() = parent.getPaddedHeight() * anchor.y + +/** + * The offset applied to the X axis according to the origin factor. + */ +val ExtendedEntity.originOffsetX: Float + get() = -(drawWidth * origin.x) + +/** + * The offset applied to the Y axis according to the origin factor. + */ +val ExtendedEntity.originOffsetY: Float + get() = -(drawHeight * origin.y) + + +/** + * Returns the draw width of the entity. + */ +fun IEntity?.getDrawWidth(): Float = when (this) { + is ExtendedEntity -> drawWidth + is IShape -> width + else -> 0f +} + +/** + * Returns the draw height of the entity. + */ +fun IEntity?.getDrawHeight(): Float = when (this) { + is ExtendedEntity -> drawHeight + is IShape -> height + else -> 0f +} + +/** + * Returns the draw X position of the entity. + */ +fun IEntity?.getDrawX(): Float = when (this) { + is ExtendedEntity -> drawX + is IShape -> x + else -> 0f +} + +/** + * Returns the draw Y position of the entity. + */ +fun IEntity?.getDrawY(): Float = when (this) { + is ExtendedEntity -> drawY + is IShape -> y + else -> 0f +} + +/** + * Attaches the entity to a parent. + */ +infix fun T.attachTo(parent: IEntity): T { + parent.attachChild(this) + return this +} \ No newline at end of file diff --git a/src/com/reco1l/andengine/EntityCollision.kt b/src/com/reco1l/andengine/EntityCollision.kt new file mode 100644 index 000000000..d96959fb3 --- /dev/null +++ b/src/com/reco1l/andengine/EntityCollision.kt @@ -0,0 +1,41 @@ +package com.reco1l.andengine + +import org.anddev.andengine.collision.ShapeCollisionChecker +import org.anddev.andengine.util.constants.Constants.VERTEX_INDEX_X +import org.anddev.andengine.util.constants.Constants.VERTEX_INDEX_Y + +object EntityCollision { + + + private val vertices = FloatArray(8) + + + fun contains(entity: ExtendedEntity, x: Float, y: Float, fromScene: Boolean): Boolean { + + val left = 0f + val top = 0f + val right = entity.drawWidth + val bottom = entity.drawHeight + + vertices[0 + VERTEX_INDEX_X] = left + vertices[0 + VERTEX_INDEX_Y] = top + + vertices[2 + VERTEX_INDEX_X] = right + vertices[2 + VERTEX_INDEX_Y] = top + + vertices[4 + VERTEX_INDEX_X] = right + vertices[4 + VERTEX_INDEX_Y] = bottom + + vertices[6 + VERTEX_INDEX_X] = left + vertices[6 + VERTEX_INDEX_Y] = bottom + + if (fromScene) { + entity.localToSceneTransformation.transform(vertices) + } else { + entity.localToParentTransformation.transform(vertices) + } + + return ShapeCollisionChecker.checkContains(vertices, vertices.size, x, y) + } + +} \ No newline at end of file diff --git a/src/com/reco1l/andengine/ExtendedEntity.kt b/src/com/reco1l/andengine/ExtendedEntity.kt index c09670497..1575dac2d 100644 --- a/src/com/reco1l/andengine/ExtendedEntity.kt +++ b/src/com/reco1l/andengine/ExtendedEntity.kt @@ -1,21 +1,20 @@ package com.reco1l.andengine -import android.graphics.PointF import android.util.* import com.reco1l.andengine.container.* import com.reco1l.andengine.modifier.* import com.reco1l.framework.* -import org.anddev.andengine.collision.* +import com.reco1l.framework.math.Vec4 import org.anddev.andengine.engine.camera.* import org.anddev.andengine.entity.* -import org.anddev.andengine.entity.primitive.* -import org.anddev.andengine.entity.scene.CameraScene import org.anddev.andengine.entity.scene.Scene +import org.anddev.andengine.entity.scene.Scene.ITouchArea import org.anddev.andengine.entity.shape.* +import org.anddev.andengine.input.touch.TouchEvent import org.anddev.andengine.opengl.util.* import org.anddev.andengine.opengl.vertex.* +import org.anddev.andengine.util.Transformation import javax.microedition.khronos.opengles.* -import javax.microedition.khronos.opengles.GL10.* /** @@ -31,6 +30,10 @@ abstract class ExtendedEntity( /** * Determines which axes the entity should automatically adjust its size to. + * + * In this case the size will equal to the content size of the entity. Some + * types of entities requires the user to manually set the size, in those + * cases this property might be ignored. */ open var autoSizeAxes = Axes.None set(value) { @@ -41,88 +44,177 @@ abstract class ExtendedEntity( } /** - * The origin factor of the entity in the X axis. + * Determines which axes the entity should adjust its size relative to its parent. + * + * Depending on the type, the entity's width and height will be taken as a multiplier + * for the parent's width and height in order to calculate the final size. + * + * Example: + * + * If [relativeSizeAxes] is set to [Axes.Both] and we set the size to 0.5, the entity's + * size will be half the size of the parent. + * + * Note: [autoSizeAxes] has priority over [relativeSizeAxes]. For example, if [autoSizeAxes] + * is set to [Axes.Both] and [relativeSizeAxes] is set to [Axes.Both], [relativeSizeAxes] + * will be ignored. */ - open var originX = 0f + open var relativeSizeAxes = Axes.None + set(value) { + if (field != value) { + field = value + onContentSizeMeasured() + } + } /** - * The origin factor of the entity in the Y axis. + * Determines which axes the entity should adjust its position relative to its parent. + * + * Depending on the type, the entity's position will be taken as a multiplier applied to + * the parent's width and height in order to calculate the final position. + * + * Example: + * + * If [relativePositionAxes] is set to [Axes.Both] and we set the position to 0.5 for both axes, + * the entity's position will be at the center of the parent. */ - open var originY = 0f + open var relativePositionAxes = Axes.None + set(value) { + if (field != value) { + field = value + invalidateTransformations() + } + } /** - * The anchor factor of the entity in the X axis. - * This is used to determine the position of the entity in a container. - * - * Note: This will not take effect if the entity is not a child of a [Container]. + * Where the entity should be anchored in the parent. */ - open var anchorX = 0f + open var anchor = Anchor.TopLeft + set(value) { + if (field != value) { + field = value + invalidateTransformations() + } + } /** - * The anchor factor of the entity in the Y axis. - * This is used to determine the position of the entity in a container. - * - * Note: This will not take effect if the entity is not a child of a [Container]. + * Where the entity's origin should be. */ - open var anchorY = 0f + open var origin = Anchor.TopLeft + set(value) { + if (field != value) { + field = value + + mRotationCenterX = value.x + mRotationCenterY = value.y + mScaleCenterX = value.x + mScaleCenterY = value.y + + invalidateTransformations() + } + } + + /** + * The padding of the entity. + */ + open var padding = Vec4.Zero + set(value) { + if (field != value) { + field = value + invalidateTransformations() + } + } /** * The translation in the X axis. */ open var translationX = 0f + set(value) { + if (field != value) { + field = value + invalidateTransformations() + } + } /** * The translation in the Y axis. */ open var translationY = 0f + set(value) { + if (field != value) { + field = value + invalidateTransformations() + } + } /** - * Whether the color should be inherited from all the parents in the hierarchy. + * The background entity. This entity will be drawn before the entity children and will not be + * affected by padding. */ - open var inheritColor = true + open var background: ExtendedEntity? = null + set(value) { + if (field != value) { + if (value?.parent != null) { + Log.e("ExtendedEntity", "The background entity is already attached to another entity.") + return + } + + field = value + } + } /** - * Whether the depth buffer should be cleared before drawing the entity. - * This is useful when the entity is drawn on top of other entities by overlapping them. - * - * It will only take effect if the entities on the front have the depth buffer test enabled. - * - * @see [testWithDepthBuffer] + * The foreground entity. This entity will be drawn after the entity children and will not be + * affected by padding. */ - open var clearDepthBufferBeforeDraw = false + open var foreground: ExtendedEntity? = null + set(value) { + if (field != value) { + if (value?.parent != null) { + Log.e("ExtendedEntity", "The foreground entity is already attached to another entity.") + return + } + + field = value + } + } + /** - * Whether the entity should be tested with the depth buffer. + * Whether the entity should clip its children. */ - open var testWithDepthBuffer = false + open var clipChildren = false /** - * The color of the entity boxed in a [ColorARGB] object. + * The depth information of the entity. */ - open var color: ColorARGB - get() = ColorARGB(mRed, mGreen, mBlue, mAlpha) - set(value) { - mRed = value.red - mGreen = value.green - mBlue = value.blue - mAlpha = value.alpha - } + open var depthInfo: DepthInfo? = null /** - * The color blending function. + * The blending information of the entity. */ - open var blendingFunction: BlendingFunction? = null + open var blendInfo: BlendInfo? = null set(value) { if (field != value) { field = value if (value != null) { - mSourceBlendFunction = value.source - mDestinationBlendFunction = value.destination + mSourceBlendFunction = value.function.source + mDestinationBlendFunction = value.function.destination } } } + /** + * The color of the entity boxed in a [ColorARGB] object. + */ + open var color: ColorARGB + get() = ColorARGB(mRed, mGreen, mBlue, mAlpha) + set(value) { + mRed = value.red + mGreen = value.green + mBlue = value.blue + mAlpha = value.alpha + } /** * The width of the content inside the entity. @@ -147,72 +239,71 @@ abstract class ExtendedEntity( } /** - * The raw X position of the entity. - * This is the position without taking into account the origin, anchor, or translation. + * The real width of the entity in pixels. + * + * Due to compatibility reason, this doesn't take into account transformations like rotation or scaling. + * @see [getWidthScaled] */ - open val drawX: Float + open val drawWidth: Float get() { - if (parent is Container) { - return (parent as Container).getChildDrawX(this) + if (relativeSizeAxes.isHorizontal) { + return parent.getPaddedWidth() * width } - return x + totalOffsetX + return width } /** - * The raw Y position of the entity. - * This is the position without taking into account the origin, anchor, or translation. + * The real height of the entity in pixels. + * + * Due to compatibility reason, this doesn't take into account transformations like rotation or scaling. + * @see [getHeightScaled] */ - open val drawY: Float + open val drawHeight: Float get() { - if (parent is Container) { - return (parent as Container).getChildDrawY(this) + if (relativeSizeAxes.isVertical) { + return parent.getPaddedHeight() * height } - return y + totalOffsetY + return height } /** - * The offset applied to the X axis according to the origin factor. + * The raw X position of the entity. + * This is the position without taking into account the origin, anchor, or translation. */ - open val originOffsetX: Float - get() = -(width * originX) + open val drawX: Float + get() { + val parent = parent + if (parent is Container) { + return parent.getChildDrawX(this) + } - /** - * The offset applied to the Y axis according to the origin factor. - */ - open val originOffsetY: Float - get() = -(height * originY) + var x = x + if (relativePositionAxes.isHorizontal) { + x *= parent.getPaddedWidth() + } - /** - * The offset applied to the X axis according to the anchor factor. - */ - open val anchorOffsetX: Float - get() = when (parent) { - is IShape -> (parent as IShape).width * anchorX - is CameraScene -> ((parent as CameraScene).camera?.widthRaw ?: 0f) * anchorX - else -> 0f + return x + totalOffsetX } - /** - * The offset applied to the Y axis according to the anchor factor. - */ - open val anchorOffsetY: Float - get() = when (parent) { - is IShape -> (parent as IShape).height * anchorY - is CameraScene -> ((parent as CameraScene).camera?.heightRaw ?: 0f) * anchorY - else -> 0f - } /** - * The total offset applied to the X axis. + * The raw Y position of the entity. + * This is the position without taking into account the origin, anchor, or translation. */ - open val totalOffsetX - get() = originOffsetX + anchorOffsetX + translationX + open val drawY: Float + get() { + val parent = parent + if (parent is Container) { + return parent.getChildDrawY(this) + } - /** - * The total offset applied to the Y axis. - */ - open val totalOffsetY - get() = originOffsetY + anchorOffsetY + translationY + var y = y + if (relativePositionAxes.isVertical) { + y *= parent.getPaddedHeight() + } + + return y + totalOffsetY + } private var width = 0f @@ -221,54 +312,64 @@ abstract class ExtendedEntity( private var isVertexBufferDirty = true + private var currentBoundEntity: ITouchArea? = null + // Attachment override fun setParent(pEntity: IEntity?) { - (parent as? Scene)?.unregisterTouchArea(this) - super.setParent(pEntity) - (pEntity as? ExtendedScene)?.registerTouchArea(this) - } + val parent = parent + if (parent is Scene) { + parent.unregisterTouchArea(this) + } - // Positions + super.setParent(pEntity) - open fun setAnchor(anchor: Anchor) { - anchorX = anchor.factorX - anchorY = anchor.factorY + if (pEntity is ExtendedScene) { + pEntity.registerTouchArea(this) + } } - open fun setOrigin(origin: Anchor) { - originX = origin.factorX - originY = origin.factorY - } - fun setPosition(position: PointF) { - setPosition(position.x, position.y) - } + // Positions - override fun setPosition(pX: Float, pY: Float) { - if (mX != pX || mY != pY) { - super.setPosition(pX, pY) + override fun setPosition(x: Float, y: Float) { + if (mX != x || mY != y) { + mX = x + mY = y + invalidateTransformations() (parent as? Container)?.onChildPositionChanged(this) } } open fun setX(value: Float) { if (mX != value) { - setPosition(value, mY) + mX = value + invalidateTransformations() + (parent as? Container)?.onChildPositionChanged(this) } } open fun setY(value: Float) { if (mY != value) { - setPosition(mX, value) + mY = value + invalidateTransformations() + (parent as? Container)?.onChildPositionChanged(this) } } open fun setTranslation(x: Float, y: Float) { - translationX = x - translationY = y + if (translationX != x || translationY != y) { + translationX = x + translationY = y + invalidateTransformations() + } + } + + protected open fun invalidateTransformations() { + mLocalToParentTransformationDirty = true + mParentToLocalTransformationDirty = true } @@ -286,22 +387,36 @@ abstract class ExtendedEntity( override fun applyRotation(pGL: GL10) { - // This will ensure getSceneCenterCoordinates() applies the correct transformation. - mRotationCenterX = width * originX - mRotationCenterY = height * originY + if (rotation == 0f) { + return + } + + val offsetX = drawWidth * mRotationCenterX + val offsetY = drawHeight * mRotationCenterY - if (rotation != 0f) { + if (offsetX > 0f || offsetY > 0f) { + pGL.glTranslatef(offsetX, offsetY, 0f) + pGL.glRotatef(rotation, 0f, 0f, 1f) + pGL.glTranslatef(-offsetX, -offsetY, 0f) + } else { pGL.glRotatef(rotation, 0f, 0f, 1f) } } override fun applyScale(pGL: GL10) { - // This will ensure getSceneCenterCoordinates() applies the correct transformation. - mScaleCenterX = width * originX - mScaleCenterY = height * originY + if (scaleX == 1f && scaleY == 1f) { + return + } + + val offsetX = drawWidth * mScaleCenterX + val offsetY = drawHeight * mScaleCenterY - if (scaleX != 1f || scaleY != 1f) { + if (offsetX > 0f || offsetY > 0f) { + pGL.glTranslatef(offsetX, offsetY, 0f) + pGL.glScalef(scaleX, scaleY, 1f) + pGL.glTranslatef(-offsetX, -offsetY, 0f) + } else { pGL.glScalef(scaleX, scaleY, 1f) } } @@ -313,23 +428,20 @@ abstract class ExtendedEntity( var blue = mBlue var alpha = mAlpha - if (inheritColor) { + var parent = parent + while (parent != null) { - var parent = parent - while (parent != null) { + red *= parent.red + green *= parent.green + blue *= parent.blue + alpha *= parent.alpha - red *= parent.red - green *= parent.green - blue *= parent.blue - alpha *= parent.alpha - - // We'll assume at this point there's no need to keep multiplying. - if (red == 0f && green == 0f && blue == 0f && alpha == 0f) { - break - } - - parent = parent.parent + // We'll assume at this point there's no need to keep multiplying. + if (red == 0f && green == 0f && blue == 0f && alpha == 0f) { + break } + + parent = parent.parent } GLHelper.setColor(pGL, red, green, blue, alpha) @@ -337,48 +449,90 @@ abstract class ExtendedEntity( protected open fun applyBlending(pGL: GL10) { - // If there's a blending function set, apply it instead of the engine's method. - val blendingFunction = blendingFunction - - if (blendingFunction != null) { + blendInfo?.apply(pGL) ?: GLHelper.blendFunction(pGL, mSourceBlendFunction, mDestinationBlendFunction) - val parent = parent as? ExtendedEntity - - // If the blending function is set to inherit, apply the parent's blending function. - if (blendingFunction == BlendingFunction.Inherit && parent != null) { + if (blendInfo?.function == BlendingFunction.Inherit) { + val parent = parent + if (parent is ExtendedEntity) { GLHelper.blendFunction(pGL, parent.mSourceBlendFunction, parent.mDestinationBlendFunction) - } else { - GLHelper.blendFunction(pGL, blendingFunction.source, blendingFunction.destination) } - - } else { - GLHelper.blendFunction(pGL, mSourceBlendFunction, mDestinationBlendFunction) } } override fun onApplyTransformations(pGL: GL10, camera: Camera) { applyTranslation(pGL, camera) - - if (rotation != 0f || scaleX != 1f || scaleY != 1f) { - pGL.glTranslatef(-originOffsetX, -originOffsetY, 0f) - applyRotation(pGL) - applyScale(pGL) - pGL.glTranslatef(originOffsetX, originOffsetY, 0f) - } - + applyRotation(pGL) + applyScale(pGL) applyColor(pGL) applyBlending(pGL) } + override fun doDraw(gl: GL10, camera: Camera) { - override fun onManagedUpdate(pSecondsElapsed: Float) { + background?.setSize(drawWidth, drawHeight) + background?.onDraw(gl, camera) + + super.doDraw(gl, camera) + } + + override fun onDrawChildren(gl: GL10, camera: Camera) { + + val hasPaddingApplicable = padding.left > 0f || padding.top > 0f + + if (hasPaddingApplicable) { + gl.glTranslatef(padding.left, padding.top, 0f) + } + + if (clipChildren) { + GLHelper.enableScissorTest(gl) + + var (bottomLeftX, bottomLeftY) = camera.getScreenSpaceCoordinates(convertLocalToSceneCoordinates(0f, 0f)) + var (topLeftX, topLeftY) = camera.getScreenSpaceCoordinates(convertLocalToSceneCoordinates(0f, getPaddedHeight())) + var (topRightX, topRightY) = camera.getScreenSpaceCoordinates(convertLocalToSceneCoordinates(getPaddedWidth(), getPaddedHeight())) + var (bottomRightX, bottomRightY) = camera.getScreenSpaceCoordinates(convertLocalToSceneCoordinates(getPaddedWidth(), 0f)) + + // Flip the Y axis to match the OpenGL coordinate system. + bottomLeftY = camera.surfaceHeight - bottomLeftY + topLeftY = camera.surfaceHeight - topLeftY + topRightY = camera.surfaceHeight - topRightY + bottomRightY = camera.surfaceHeight - bottomRightY + + val minClippingX = minOf(bottomLeftX, topLeftX, topRightX, bottomRightX) + val minClippingY = minOf(bottomLeftY, topLeftY, topRightY, bottomRightY) + + val maxClippingX = maxOf(bottomLeftX, topLeftX, topRightX, bottomRightX) + val maxClippingY = maxOf(bottomLeftY, topLeftY, topRightY, bottomRightY) + + gl.glScissor( + minClippingX.toInt(), + minClippingY.toInt(), + (maxClippingX - minClippingX).toInt(), + (maxClippingY - minClippingY).toInt() + ) + } + + super.onDrawChildren(gl, camera) + + if (clipChildren) { + GLHelper.disableScissorTest(gl) + } + + if (hasPaddingApplicable) { + gl.glTranslatef(-padding.right, -padding.top, 0f) + } + + foreground?.setSize(drawWidth, drawHeight) + foreground?.onDraw(gl, camera) + } + + override fun onManagedDraw(gl: GL10, camera: Camera) { if (isVertexBufferDirty) { isVertexBufferDirty = false onUpdateVertexBuffer() } - super.onManagedUpdate(pSecondsElapsed) + super.onManagedDraw(gl, camera) } override fun onInitDraw(pGL: GL10) { @@ -387,22 +541,26 @@ abstract class ExtendedEntity( GLHelper.enableVertexArray(pGL) } - if (clearDepthBufferBeforeDraw) { - pGL.glClear(GL_DEPTH_BUFFER_BIT) - } - - GLHelper.setDepthTest(pGL, testWithDepthBuffer) + depthInfo?.apply(pGL) ?: GLHelper.disableDepthTest(pGL) } override fun onApplyVertices(pGL: GL10) { - if (vertexBuffer != null) { super.onApplyVertices(pGL) } - } + // Update + + override fun onManagedUpdate(pSecondsElapsed: Float) { + + background?.onManagedUpdate(pSecondsElapsed) + foreground?.onManagedUpdate(pSecondsElapsed) + + super.onManagedUpdate(pSecondsElapsed) + } + // Vertex buffer @@ -415,7 +573,6 @@ abstract class ExtendedEntity( onUpdateVertexBuffer() } - /** * Sets the vertex buffer of the entity. * @@ -448,15 +605,24 @@ abstract class ExtendedEntity( if (contentWidth != width || contentHeight != height) { - if (autoSizeAxes == Axes.X || autoSizeAxes == Axes.Both) { - width = contentWidth + if (autoSizeAxes.isHorizontal) { + width = contentWidth + padding.horizontal + + if (relativeSizeAxes.isHorizontal) { + width /= parent.getPaddedWidth() + } } - if (autoSizeAxes == Axes.Y || autoSizeAxes == Axes.Both) { - height = contentHeight + if (autoSizeAxes.isVertical) { + height = contentHeight + padding.vertical + + if (relativeSizeAxes.isVertical) { + height /= parent.getPaddedHeight() + } } updateVertexBuffer() + invalidateTransformations() (parent as? Container)?.onChildSizeChanged(this) return true @@ -464,52 +630,83 @@ abstract class ExtendedEntity( return false } + /** * Sets the size of the entity. * - * @return Whether the size of the entity was changed or not, this depends on the [autoSizeAxes] property. + * Note: This will change the [autoSizeAxes] property to [Axes.None] automatically. + * + * @return Whether the size of the entity was changed or not. */ open fun setSize(newWidth: Float, newHeight: Float): Boolean { - if (autoSizeAxes == Axes.Both) { - Log.w("ExtendedEntity", "Cannot set size when autoSizeAxes is set to Both.") - return false + if (autoSizeAxes != Axes.None) { + autoSizeAxes = Axes.None } if (width != newWidth || height != newHeight) { + width = newWidth + height = newHeight - if (autoSizeAxes == Axes.None || autoSizeAxes == Axes.Y) { - width = newWidth - } + updateVertexBuffer() + invalidateTransformations() - if (autoSizeAxes == Axes.None || autoSizeAxes == Axes.X) { - height = newHeight + val parent = parent + if (parent is Container) { + parent.onChildSizeChanged(this) } - updateVertexBuffer() - - (parent as? Container)?.onChildSizeChanged(this) return true } return false } open fun setWidth(value: Float) { - setSize(value, height) - } - override fun getWidth(): Float { - return width + if (autoSizeAxes.isHorizontal) { + autoSizeAxes = if (autoSizeAxes == Axes.Both) Axes.Y else Axes.None + } + + if (width != value) { + width = value + + updateVertexBuffer() + invalidateTransformations() + (parent as? Container)?.onChildSizeChanged(this) + } } open fun setHeight(value: Float) { - setSize(width, value) + + if (autoSizeAxes.isVertical) { + autoSizeAxes = if (autoSizeAxes == Axes.Both) Axes.X else Axes.None + } + + if (height != value) { + height = value + + updateVertexBuffer() + invalidateTransformations() + (parent as? Container)?.onChildSizeChanged(this) + } + } + + override fun getWidth(): Float { + return width } override fun getHeight(): Float { return height } + override fun getWidthScaled(): Float { + return drawWidth * scaleX + } + + override fun getHeightScaled(): Float { + return drawHeight * scaleY + } + // Unsupported methods @@ -519,57 +716,103 @@ abstract class ExtendedEntity( @Deprecated("Base height is not preserved in ExtendedEntity, use getHeight() instead.") override fun getBaseHeight() = height - @Deprecated("Rotation center is determined by the entity's origin, use setOrigin() instead.") - final override fun setRotationCenter(pRotationCenterX: Float, pRotationCenterY: Float) {} - @Deprecated("Rotation center is determined by the entity's origin, use setOrigin() instead.") - final override fun setRotationCenterX(pRotationCenterX: Float) {} + // Collision + + override fun collidesWith(shape: IShape): Boolean { + Log.w("ExtendedEntity", "Collision detection is not supported in ExtendedEntity.") + return false + } + + override fun contains(x: Float, y: Float): Boolean { - @Deprecated("Rotation center is determined by the entity's origin, use setOrigin() instead.") - final override fun setRotationCenterY(pRotationCenterY: Float) {} + if (drawWidth == 0f || drawHeight == 0f) { + return false + } + + return EntityCollision.contains(this, x, y, parent is Scene) + } - @Deprecated("Scale center is determined by the entity's origin, use setOrigin() instead.") - final override fun setScaleCenter(pScaleCenterX: Float, pScaleCenterY: Float) {} + override fun isCulled(pCamera: Camera): Boolean { + return drawX > pCamera.maxX || drawX + drawWidth < pCamera.minX + || drawY > pCamera.maxY || drawY + drawHeight < pCamera.minY + } - @Deprecated("Scale center is determined by the entity's origin, use setOrigin() instead.") - final override fun setScaleCenterX(pScaleCenterX: Float) {} + // Transformation - @Deprecated("Scale center is determined by the entity's origin, use setOrigin() instead.") - final override fun setScaleCenterY(pScaleCenterY: Float) {} + override fun getLocalToParentTransformation(): Transformation { + if (mLocalToParentTransformation == null) { + mLocalToParentTransformation = Transformation() + } - // Collision + if (mLocalToParentTransformationDirty) { + mLocalToParentTransformation.setToIdentity() - override fun collidesWith(shape: IShape): Boolean = when (shape) { + if (scaleX != 1f || scaleY != 1f) { + val offsetX = drawWidth * mScaleCenterX + val offsetY = drawHeight * mScaleCenterY - is RectangularShape -> RectangularShapeCollisionChecker.checkCollision(this, shape) - is Line -> RectangularShapeCollisionChecker.checkCollision(this, shape) + mLocalToParentTransformation.postTranslate(-offsetX, -offsetY) + mLocalToParentTransformation.postScale(scaleX, scaleY) + mLocalToParentTransformation.postTranslate(offsetX, offsetY) + } - else -> false - } + if (rotation != 0f) { + val offsetX = drawWidth * mRotationCenterX + val offsetY = drawHeight * mRotationCenterY - override fun contains(x: Float, y: Float): Boolean { - if (width == 0f || height == 0f) { - return false + mLocalToParentTransformation.postTranslate(-offsetX, -offsetY) + mLocalToParentTransformation.postRotate(rotation) + mLocalToParentTransformation.postTranslate(offsetX, offsetY) + } + + mLocalToParentTransformation.postTranslate(drawX, drawY) + mLocalToParentTransformationDirty = false } - return RectangularShapeCollisionChecker.checkContains(this, x - totalOffsetX, y - totalOffsetY) + return mLocalToParentTransformation } - override fun isCulled(pCamera: Camera): Boolean { - return drawX > pCamera.maxX || drawX + width < pCamera.minX - || drawY > pCamera.maxY || drawY + height < pCamera.minY - } + override fun getParentToLocalTransformation(): Transformation { - override fun getSceneCenterCoordinates(): FloatArray { - return this.convertLocalToSceneCoordinates(width * 0.5f, height * 0.5f) - } + if (mParentToLocalTransformation == null) { + mParentToLocalTransformation = Transformation() + } + if (mParentToLocalTransformationDirty) { + mParentToLocalTransformation.setToIdentity() + mParentToLocalTransformation.postTranslate(-drawX, -drawY) + + if (rotation != 0f) { + val offsetX = drawWidth * mRotationCenterX + val offsetY = drawHeight * mRotationCenterY + + mParentToLocalTransformation.postTranslate(-offsetX, -offsetY) + mParentToLocalTransformation.postRotate(-rotation) + mParentToLocalTransformation.postTranslate(offsetX, offsetY) + } + + if (scaleX != 1f || scaleY != 1f) { + val offsetX = drawWidth * mScaleCenterX + val offsetY = drawHeight * mScaleCenterY + + mParentToLocalTransformation.postTranslate(-offsetX, -offsetY) + mParentToLocalTransformation.postScale(1 / scaleX, 1 / scaleY) + mParentToLocalTransformation.postTranslate(offsetX, offsetY) + } + + mParentToLocalTransformationDirty = false + } + + return mParentToLocalTransformation + } - // Transformation override fun setBlendFunction(pSourceBlendFunction: Int, pDestinationBlendFunction: Int) { - blendingFunction = null + if (blendInfo != null) { + Log.w("ExtendedEntity", "BlendInfo is set, use blendInfo property to change the blending function.") + } super.setBlendFunction(pSourceBlendFunction, pDestinationBlendFunction) } @@ -583,6 +826,51 @@ abstract class ExtendedEntity( return modifier } -} + // Input + override fun onAreaTouched( + event: TouchEvent, + localX: Float, + localY: Float + ): Boolean { + + val boundEntity = currentBoundEntity + if (boundEntity != null) { + boundEntity as IEntity + + val transformedX = localX - boundEntity.getDrawX() + val transformedY = localY - boundEntity.getDrawY() + + boundEntity.onAreaTouched(event, transformedX, transformedY) + + if (event.isActionUp || event.isActionOutside || event.isActionCancel) { + currentBoundEntity = null + } + return true + } + + try { + for (i in childCount - 1 downTo 0) { + val child = getChild(i) + + if (child is ITouchArea && child.contains(localX, localY)) { + + val transformedX = localX - child.getDrawX() + val transformedY = localY - child.getDrawY() + + if (child.onAreaTouched(event, transformedX, transformedY)) { + if (event.isActionDown) { + currentBoundEntity = child + } + return true + } + } + } + } catch (e: IndexOutOfBoundsException) { + Log.e("ExtendedEntity", "A child entity was removed during touch event propagation.", e) + } + return false + } + +} diff --git a/src/com/reco1l/andengine/ExtendedScene.kt b/src/com/reco1l/andengine/ExtendedScene.kt index f61155e20..5111da013 100644 --- a/src/com/reco1l/andengine/ExtendedScene.kt +++ b/src/com/reco1l/andengine/ExtendedScene.kt @@ -28,6 +28,7 @@ open class ExtendedScene : Scene(), IShape { init { super.setTouchAreaBindingEnabled(true) + super.setOnAreaTouchTraversalFrontToBack() } diff --git a/src/com/reco1l/andengine/container/ConstraintContainer.kt b/src/com/reco1l/andengine/container/ConstraintContainer.kt index 70d08da6c..159257aca 100644 --- a/src/com/reco1l/andengine/container/ConstraintContainer.kt +++ b/src/com/reco1l/andengine/container/ConstraintContainer.kt @@ -9,7 +9,7 @@ import org.anddev.andengine.entity.shape.* * * This is useful for creating complex layouts. */ -class ConstraintContainer : Container() { +open class ConstraintContainer : Container() { private val constraints = mutableMapOf() @@ -17,18 +17,52 @@ class ConstraintContainer : Container() { override fun getChildDrawX(child: ExtendedEntity): Float { - val constraint = constraints[child] ?: this - val anchorOffsetX = constraint.width * child.anchorX + val target = constraints[child] ?: this - return child.x + child.originOffsetX + anchorOffsetX + child.translationX + var targetX = target.getDrawX() + var targetWidth = target.getDrawWidth() + + if (target == this) { + targetX = 0f + targetWidth = getPaddedWidth() + } + + val anchorOffsetX = targetWidth * child.anchor.x + + var childX = child.x + if (child.relativePositionAxes.isHorizontal) { + + // Relative positions will be multiplied by the remaining space from the + // target's position to the edge of the container. + childX *= getPaddedWidth() - targetX + } + + return targetX + childX + child.originOffsetX + anchorOffsetX + child.translationX } override fun getChildDrawY(child: ExtendedEntity): Float { - val constraint = constraints[child] ?: this - val anchorOffsetY = constraint.height * child.anchorY + val target = constraints[child] ?: this + + var targetY = target.getDrawY() + var targetHeight = target.getDrawHeight() + + if (target == this) { + targetY = 0f + targetHeight = getPaddedHeight() + } + + val anchorOffsetY = targetHeight * child.anchor.y + + var childY = child.y + if (child.relativePositionAxes.isVertical) { + + // Relative positions will be multiplied by the remaining space from the + // target's position to the edge of the container. + childY *= getPaddedHeight() - targetY + } - return child.y + child.originOffsetY + anchorOffsetY + child.translationY + return targetY + childY + child.originOffsetY + anchorOffsetY + child.translationY } diff --git a/src/com/reco1l/andengine/container/Container.kt b/src/com/reco1l/andengine/container/Container.kt index 611f1554b..2364427d5 100644 --- a/src/com/reco1l/andengine/container/Container.kt +++ b/src/com/reco1l/andengine/container/Container.kt @@ -1,14 +1,10 @@ package com.reco1l.andengine.container -import android.util.* import com.reco1l.andengine.* import com.reco1l.toolkt.kotlin.* import org.anddev.andengine.engine.camera.* import org.anddev.andengine.entity.* import org.anddev.andengine.entity.IEntity.* -import org.anddev.andengine.entity.scene.Scene.ITouchArea -import org.anddev.andengine.entity.shape.IShape -import org.anddev.andengine.input.touch.* import org.anddev.andengine.util.* import javax.microedition.khronos.opengles.GL10 import kotlin.math.* @@ -62,11 +58,23 @@ open class Container : ExtendedEntity() { open fun getChildDrawX(child: ExtendedEntity): Float { - return child.x + child.totalOffsetX + + var x = child.x + if (child.relativePositionAxes.isHorizontal) { + x *= getPaddedWidth() + } + + return x + child.totalOffsetX } open fun getChildDrawY(child: ExtendedEntity): Float { - return child.y + child.totalOffsetY + + var y = child.y + if (child.relativePositionAxes.isVertical) { + y *= getPaddedHeight() + } + + return y + child.totalOffsetY } @@ -86,21 +94,11 @@ open class Container : ExtendedEntity() { val child = mChildren.getOrNull(i) ?: continue - var offsetX = child.x - var offsetY = child.y - - if (child is ExtendedEntity) { - offsetX += child.originOffsetX - offsetY += child.originOffsetY - } - - offsetX = max(0f, offsetX) - offsetY = max(0f, offsetY) + val x = max(0f, child.getDrawX()) + val y = max(0f, child.getDrawY()) - if (child is IShape) { - contentWidth = max(contentWidth, offsetX + child.width) - contentHeight = max(contentHeight, offsetY + child.height) - } + contentWidth = max(contentWidth, x + child.getDrawWidth()) + contentHeight = max(contentHeight, y + child.getDrawHeight()) } } @@ -166,24 +164,22 @@ open class Container : ExtendedEntity() { override fun onUpdateVertexBuffer() {} override fun drawVertices(pGL: GL10, pCamera: Camera) {} +} - // Input - override fun onAreaTouched(event: TouchEvent, localX: Float, localY: Float): Boolean { +operator fun Container.get(index: Int): T { + @Suppress("UNCHECKED_CAST") + return getChild(index) as T +} - if (mChildren != null) { - try { - mChildren.fastForEach { - if (it is ITouchArea && it.contains(localX, localY)) { - return it.onAreaTouched(event, localX, localY) - } - } - } catch (e: IndexOutOfBoundsException) { - Log.e("Container", "A child entity was removed during touch event propagation.") - } - } +operator fun Container.set(index: Int, entity: IEntity) { + attachChild(entity, index) +} - return false - } +operator fun Container.plusAssign(entity: IEntity) { + attachChild(entity) } +operator fun Container.minusAssign(entity: IEntity) { + detachChild(entity) +} \ No newline at end of file diff --git a/src/com/reco1l/andengine/container/LinearContainer.kt b/src/com/reco1l/andengine/container/LinearContainer.kt index 80240b4f9..3c4e4abc1 100644 --- a/src/com/reco1l/andengine/container/LinearContainer.kt +++ b/src/com/reco1l/andengine/container/LinearContainer.kt @@ -30,38 +30,40 @@ open class LinearContainer : Container() { } - private var lastDrawOffset = 0f - - override fun onMeasureContentSize() { shouldMeasureSize = false contentWidth = 0f contentHeight = 0f - if (mChildren != null) { + for (i in 0 until childCount) { - for (i in mChildren.indices) { + val child = getChild(i) ?: continue + if (child !is ExtendedEntity) { + continue + } - val child = mChildren.getOrNull(i) ?: continue + when (orientation) { - // Non-shape children are ignored as they doesn't have a size there's nothing to do. - if (child !is IShape) { - continue - } + Horizontal -> { + child.x = contentWidth - val spacing = if (i == mChildren.size - 1) 0f else spacing + contentWidth += child.getDrawWidth() + contentHeight = max(contentHeight, child.getDrawHeight()) - when (orientation) { - - Horizontal -> { - contentWidth += child.width + spacing - contentHeight = max(contentHeight, child.height) + if (i < childCount - 1) { + contentWidth += spacing } + } + + Vertical -> { + child.y = contentHeight - Vertical -> { - contentWidth = max(contentWidth, child.width) - contentHeight += child.height + spacing + contentWidth = max(contentWidth, child.getDrawWidth()) + contentHeight += child.getDrawHeight() + + if (i < childCount - 1) { + contentHeight += spacing } } } @@ -71,33 +73,28 @@ open class LinearContainer : Container() { } - override fun onManagedDrawChildren(pGL: GL10, pCamera: Camera) { - lastDrawOffset = 0f - super.onManagedDrawChildren(pGL, pCamera) - } - override fun getChildDrawX(child: ExtendedEntity): Float { + val drawX = super.getChildDrawX(child) + if (orientation == Vertical) { - return super.getChildDrawX(child) + return drawX } - val drawX = lastDrawOffset + super.getChildDrawX(child) - lastDrawOffset += child.width + spacing - - return drawX + // Subtract the anchor offset for the X axis because it should be ignored in this case. + return drawX - child.anchorOffsetX } override fun getChildDrawY(child: ExtendedEntity): Float { + val drawY = super.getChildDrawY(child) + if (orientation == Horizontal) { - return super.getChildDrawY(child) + return drawY } - val drawY = lastDrawOffset + super.getChildDrawY(child) - lastDrawOffset += child.height + spacing - - return drawY + // Subtract the anchor offset for the Y axis because it should be ignored in this case. + return drawY - child.anchorOffsetY } } diff --git a/src/com/reco1l/andengine/container/ScrollableContainer.kt b/src/com/reco1l/andengine/container/ScrollableContainer.kt index 45afd5aa5..c6cf72417 100644 --- a/src/com/reco1l/andengine/container/ScrollableContainer.kt +++ b/src/com/reco1l/andengine/container/ScrollableContainer.kt @@ -3,6 +3,7 @@ package com.reco1l.andengine.container import com.reco1l.andengine.* import com.reco1l.andengine.shape.* import com.reco1l.framework.* +import com.rian.osu.math.Precision import org.anddev.andengine.engine.camera.* import org.anddev.andengine.input.touch.* import org.anddev.andengine.input.touch.TouchEvent.* @@ -21,9 +22,18 @@ open class ScrollableContainer : Container() { */ var scrollX = 0f set(value) { - if (scrollAxes == Axes.Both || scrollAxes == Axes.X) { - field = value + if (Precision.almostEquals(value, 0f) || !scrollAxes.isHorizontal) { + field = 0f + return } + + if (Precision.almostEquals(value, maxScrollX)) { + field = maxScrollX + return + } + + indicatorX?.alpha = 0.5f + field = value } /** @@ -31,9 +41,18 @@ open class ScrollableContainer : Container() { */ var scrollY = 0f set(value) { - if (scrollAxes == Axes.Both || scrollAxes == Axes.Y) { - field = value + if (Precision.almostEquals(value, 0f) || !scrollAxes.isVertical) { + field = 0f + return } + + if (Precision.almostEquals(value, maxScrollY)) { + field = maxScrollY + return + } + + indicatorY?.alpha = 0.5f + field = value } /** @@ -50,21 +69,25 @@ open class ScrollableContainer : Container() { /** * The maximum velocity in px/s on the x-axis. */ - var maxVelocityX = 10000f + var maxVelocityX = 5000f /** * The maximum velocity in px/s on the y-axis. */ - var maxVelocityY = 10000f + var maxVelocityY = 5000f /** * The velocity in px/s on the x-axis. */ var velocityX = 0f private set(value) { - if (scrollAxes == Axes.Both || scrollAxes == Axes.X) { - field = value.coerceIn(-maxVelocityX, maxVelocityX) + + if (Precision.almostEquals(value, 0f) || !scrollAxes.isHorizontal) { + field = 0f + return } + + field = value.coerceIn(-maxVelocityX, maxVelocityX) } /** @@ -72,9 +95,13 @@ open class ScrollableContainer : Container() { */ var velocityY = 0f private set(value) { - if (scrollAxes == Axes.Both || scrollAxes == Axes.Y) { - field = value.coerceIn(-maxVelocityY, maxVelocityY) + + if (Precision.almostEquals(value, 0f) || !scrollAxes.isVertical) { + field = 0f + return } + + field = value.coerceIn(-maxVelocityY, maxVelocityY) } /** @@ -104,7 +131,7 @@ open class ScrollableContainer : Container() { * This does not take into account the overscroll. */ val maxScrollX - get() = max(0f, scrollableContentWidth - width) + get() = max(0f, scrollableContentWidth - drawWidth) /** * The maximum scroll position on the y-axis. @@ -112,21 +139,21 @@ open class ScrollableContainer : Container() { * This does not take into account the overscroll. */ val maxScrollY - get() = max(0f, scrollableContentHeight - height) + get() = max(0f, scrollableContentHeight - drawHeight) /** * The width of the content that can be scrolled. That is [contentWidth] minus * the width of the vertical indicator. */ val scrollableContentWidth - get() = max(0f, contentWidth - (indicatorY?.width ?: 0f)) + get() = max(0f, contentWidth - (indicatorY?.drawWidth ?: 0f)) /** * The height of the content that can be scrolled. That is [contentHeight] minus * the height of the horizontal indicator. */ val scrollableContentHeight - get() = max(0f, contentHeight - (indicatorX?.height ?: 0f)) + get() = max(0f, contentHeight - (indicatorX?.drawHeight ?: 0f)) private var initialX = 0f @@ -143,10 +170,8 @@ open class ScrollableContainer : Container() { super.onManagedUpdate(deltaTimeSec) // Seems like AndEngine doesn't handle ACTION_OUTSIDE events properly so we have to set a dragging timeout. - if (isDragging && elapsedTimeSec - lastDragTimeSec > 1f) { + if (isDragging && elapsedTimeSec - lastDragTimeSec > 0.1f) { isDragging = false - velocityX = 0f - velocityY = 0f } if (!isDragging && (velocityX != 0f || velocityY != 0f)) { @@ -156,14 +181,6 @@ open class ScrollableContainer : Container() { velocityX *= deceleration velocityY *= deceleration - - if (abs(velocityX) < INSIGNIFICANT_DISTANCE) { - velocityX = 0f - } - - if (abs(velocityY) < INSIGNIFICANT_DISTANCE) { - velocityY = 0f - } } // Back smoothly to the max scroll position if the scroll position is out of bounds. @@ -171,82 +188,56 @@ open class ScrollableContainer : Container() { if (scrollX > maxScrollX) { velocityX = 0f - - val deltaX = scrollX - maxScrollX - scrollX -= deltaX * 0.1f - - if (abs(deltaX) < INSIGNIFICANT_DISTANCE) { - scrollX = maxScrollX - } + scrollX -= (scrollX - maxScrollX) * 0.1f } if (scrollY > maxScrollY) { velocityY = 0f - - val deltaY = scrollY - maxScrollY - scrollY -= deltaY * 0.1f - - if (abs(deltaY) < INSIGNIFICANT_DISTANCE) { - scrollY = maxScrollY - } + scrollY -= (scrollY - maxScrollY) * 0.1f } if (scrollY < 0) { velocityY = 0f scrollY += -scrollY * 0.1f - - if (abs(scrollY) < INSIGNIFICANT_DISTANCE) { - scrollY = 0f - } } if (scrollX < 0) { velocityX = 0f scrollX += -scrollX * 0.1f - - if (abs(scrollX) < INSIGNIFICANT_DISTANCE) { - scrollX = 0f - } } } // Updating progress indicators - indicatorY?.let { + indicatorY?.let { indicator -> - it.isVisible = scrollAxes == Axes.Both || scrollAxes == Axes.Y - it.y = scrollY * (height / scrollableContentHeight) + indicator.isVisible = scrollAxes == Axes.Both || scrollAxes == Axes.Y - if (it.alpha > 0f && velocityY == 0f) { - it.alpha -= deltaTimeSec * 0.5f + indicator.x = drawWidth - indicator.drawWidth + indicator.y = scrollY * (drawHeight / scrollableContentHeight) - if (it.alpha < 0f) { - it.alpha = 0f - } + if (indicator.alpha > 0f && velocityY == 0f) { + indicator.alpha = (indicator.alpha - deltaTimeSec * 0.5f).coerceAtLeast(0f) } - if (it.isVisible) { - it.onUpdate(deltaTimeSec) + if (indicator.isVisible) { + indicator.onUpdate(deltaTimeSec) } } - indicatorX?.let { - - it.isVisible = scrollAxes == Axes.Both || scrollAxes == Axes.X + indicatorX?.let { indicator -> - it.x = scrollX * (width / scrollableContentWidth) - it.y = height - it.height + indicator.isVisible = scrollAxes == Axes.Both || scrollAxes == Axes.X - if (it.alpha > 0f && velocityX == 0f) { - it.alpha -= deltaTimeSec * 0.5f + indicator.x = scrollX * (drawWidth / scrollableContentWidth) + indicator.y = drawHeight - indicator.drawHeight - if (it.alpha < 0f) { - it.alpha = 0f - } + if (indicator.alpha > 0f && velocityX == 0f) { + indicator.alpha = (indicator.alpha - deltaTimeSec * 0.5f).coerceAtLeast(0f) } - if (it.isVisible) { - it.onUpdate(deltaTimeSec) + if (indicator.isVisible) { + indicator.onUpdate(deltaTimeSec) } } @@ -257,18 +248,20 @@ open class ScrollableContainer : Container() { override fun onMeasureContentSize() { super.onMeasureContentSize() - indicatorY?.let { - it.height = height * (height / scrollableContentHeight) - it.x = contentWidth + 5f + indicatorY?.let { indicator -> + + indicator.height = drawHeight * (drawHeight / scrollableContentHeight) + indicator.x = contentWidth + 5f - contentWidth += it.width + 5f + contentWidth += indicator.drawWidth + 5f } - indicatorX?.let { - it.width = width * (width / scrollableContentWidth) - it.y = contentHeight + 5f + indicatorX?.let { indicator -> - contentHeight += it.height + 5f + indicator.width = drawWidth * (drawWidth / scrollableContentWidth) + indicator.y = contentHeight + 5f + + contentHeight += indicator.drawHeight + 5f } if (indicatorX != null || indicatorY != null) { @@ -276,9 +269,8 @@ open class ScrollableContainer : Container() { } } - - override fun onDrawChildren(pGL: GL10, pCamera: Camera) { - super.onDrawChildren(pGL, pCamera) + override fun onManagedDrawChildren(pGL: GL10, pCamera: Camera) { + super.onManagedDrawChildren(pGL, pCamera) indicatorX?.onDraw(pGL, pCamera) indicatorY?.onDraw(pGL, pCamera) @@ -289,73 +281,69 @@ open class ScrollableContainer : Container() { when (event.action) { ACTION_DOWN -> { - isDragging = true - - initialX = event.x - initialY = event.y + initialX = localX + initialY = localY velocityX = 0f velocityY = 0f - - lastDragTimeSec = elapsedTimeSec } - ACTION_UP, ACTION_CANCEL, ACTION_OUTSIDE -> { - isDragging = false - } + ACTION_MOVE -> { - else -> { - isDragging = true + var deltaX = if (scrollAxes.isHorizontal) localX - initialX else 0f + var deltaY = if (scrollAxes.isVertical) localY - initialY else 0f + + if (!isDragging) { + isDragging = abs(deltaX) > 1f || abs(deltaY) > 1f - // Coerce the delta values to the width and height of the container because the user can't scroll more than that. - var deltaX = (event.x - initialX).coerceAtMost(width) - var deltaY = (event.y - initialY).coerceAtMost(height) + if (!isDragging) { + return super.onAreaTouched(event, localX, localY) + } + } val length = hypot(deltaX, deltaY) - // Slow down the scroll when reaching the bounds. - if (scrollX + deltaX < 0 || scrollX + deltaX > maxScrollX) { + if (scrollX - deltaX < 0 || scrollX - deltaX > maxScrollX) { deltaX *= if (length <= 0) 0f else length.pow(0.7f) / length } - if (scrollY + deltaY < 0 || scrollY + deltaY > maxScrollY) { + if (scrollY - deltaY < 0 || scrollY - deltaY > maxScrollY) { deltaY *= if (length <= 0) 0f else length.pow(0.7f) / length } - if (deltaX.isNaN() || abs(deltaX) < INSIGNIFICANT_DISTANCE) { - deltaX = 0f - } + val dragTimeSec = elapsedTimeSec - lastDragTimeSec - if (deltaY.isNaN() || abs(deltaY) < INSIGNIFICANT_DISTANCE) { - deltaY = 0f + if (abs(deltaX) > 0.1f) { + scrollX -= deltaX + velocityX = abs(deltaX / dragTimeSec) * sign(deltaX) + initialX = localX } - if (deltaX != 0f || deltaY != 0f) { - indicatorX?.alpha = 0.5f - indicatorY?.alpha = 0.5f + if (abs(deltaY) > 0.1f) { + scrollY -= deltaY + velocityY = abs(deltaY / dragTimeSec) * sign(deltaY) + initialY = localY } - scrollX -= deltaX - scrollY -= deltaY - - val dragTime = elapsedTimeSec - lastDragTimeSec - velocityX = deltaX / dragTime - velocityY = deltaY / dragTime - - initialX = event.x - initialY = event.y - lastDragTimeSec = elapsedTimeSec } + + else -> { + isDragging = false + } + } + + if (!isDragging) { + return super.onAreaTouched(event, localX, localY) } - return super.onAreaTouched(event, localX, localY) + return true } override fun getChildDrawX(child: ExtendedEntity): Float { - if (child == indicatorX || child == indicatorY) { + if (child == indicatorX || child == indicatorY || !scrollAxes.isHorizontal) { return super.getChildDrawX(child) } @@ -364,7 +352,7 @@ open class ScrollableContainer : Container() { override fun getChildDrawY(child: ExtendedEntity): Float { - if (child == indicatorX || child == indicatorY) { + if (child == indicatorX || child == indicatorY || !scrollAxes.isVertical) { return super.getChildDrawY(child) } @@ -376,7 +364,5 @@ open class ScrollableContainer : Container() { const val DEFAULT_DECELERATION = 0.98f - const val INSIGNIFICANT_DISTANCE = 0.05f - } } \ No newline at end of file diff --git a/src/com/reco1l/andengine/shape/Box.kt b/src/com/reco1l/andengine/shape/Box.kt index 39443c396..041ef7d79 100644 --- a/src/com/reco1l/andengine/shape/Box.kt +++ b/src/com/reco1l/andengine/shape/Box.kt @@ -26,7 +26,7 @@ open class Box : ExtendedEntity(vertexBuffer = BoxVertexBuffer()) { override fun onUpdateVertexBuffer() { - (vertexBuffer as BoxVertexBuffer).update(width, height) + (vertexBuffer as BoxVertexBuffer).update(drawWidth, drawHeight) } override fun drawVertices(gl: GL10, camera: Camera) { @@ -59,7 +59,7 @@ open class Box : ExtendedEntity(vertexBuffer = BoxVertexBuffer()) { } -open class RoundedBox(segmentsPerArc: Int = 10) : ExtendedEntity(RoundedBoxVertexBuffer(segmentsPerArc)) { +open class RoundedBox : ExtendedEntity() { /** * The corner radius of the rectangle. @@ -103,7 +103,8 @@ open class RoundedBox(segmentsPerArc: Int = 10) : ExtendedEntity(RoundedBoxVerte override fun onUpdateVertexBuffer() { - val cornerRadius = cornerRadius.coerceIn(0f, min(width, height) / 2f) + val smallerSide = min(drawWidth, drawHeight) + val cornerRadius = cornerRadius.coerceAtMost(smallerSide / 2f).coerceAtLeast(0f) if (shouldRebuildVertexBuffer) { shouldRebuildVertexBuffer = false @@ -114,7 +115,7 @@ open class RoundedBox(segmentsPerArc: Int = 10) : ExtendedEntity(RoundedBoxVerte setVertexBuffer(RoundedBoxVertexBuffer(segmentsPerArc)) } - (vertexBuffer as RoundedBoxVertexBuffer).update(width, height, cornerRadius) + (vertexBuffer as RoundedBoxVertexBuffer).update(drawWidth, drawHeight, cornerRadius) } override fun drawVertices(pGL: GL10, pCamera: Camera) { @@ -124,7 +125,7 @@ open class RoundedBox(segmentsPerArc: Int = 10) : ExtendedEntity(RoundedBoxVerte class RoundedBoxVertexBuffer(private val segmentsPerArc: Int) : VertexBuffer( - (5 /*Quads*/ * 4 + (segmentsPerArc + 2) /*Arcs*/ * 4) * 2, + (3 /*Quads*/ * 4 + (segmentsPerArc + 2) /*Arcs*/ * 4) * 2, GL11.GL_STATIC_DRAW, false ) { @@ -151,41 +152,28 @@ open class RoundedBox(segmentsPerArc: Int = 10) : ExtendedEntity(RoundedBoxVerte } // Quads: - // [1] - // [4] [5] [2] - // [3] + // [ ] + // [1] [2] [3] + // [ ] // [1] addQuad( - fromX = cornerRadius, fromY = 0f, - toX = width - cornerRadius, toY = cornerRadius + fromX = 0f, fromY = cornerRadius, + toX = cornerRadius, toY = height - cornerRadius ) // [2] addQuad( - fromX = width - cornerRadius, fromY = cornerRadius, - toX = width, toY = height - cornerRadius - ) - - // [3] - addQuad( - fromX = cornerRadius, fromY = height - cornerRadius, + fromX = cornerRadius, fromY = 0f, toX = width - cornerRadius, toY = height ) - // [4] - addQuad( - fromX = 0f, fromY = cornerRadius, - toX = cornerRadius, toY = height - cornerRadius - ) - - // [5] + // [3] addQuad( - fromX = cornerRadius, fromY = cornerRadius, - toX = width - cornerRadius, toY = height - cornerRadius + fromX = width - cornerRadius, fromY = cornerRadius, + toX = width, toY = height - cornerRadius ) - // Arcs fun addArc(centerX: Float, centerY: Float, startAngle: Float, endAngle: Float) { @@ -234,7 +222,7 @@ open class RoundedBox(segmentsPerArc: Int = 10) : ExtendedEntity(RoundedBoxVerte var offset = 0 // Quads - for (i in 0 until 5) { + for (i in 0 until 3) { gl.glDrawArrays(GL_TRIANGLE_STRIP, offset, 4) offset += 4 } diff --git a/src/com/reco1l/andengine/shape/Circle.kt b/src/com/reco1l/andengine/shape/Circle.kt index 51ec4ba1d..f2ec7b64b 100644 --- a/src/com/reco1l/andengine/shape/Circle.kt +++ b/src/com/reco1l/andengine/shape/Circle.kt @@ -16,7 +16,7 @@ import kotlin.math.* * * @author Reco1l */ -class Circle : ExtendedEntity() { +open class Circle : ExtendedEntity() { /** * The angle where the circle starts to draw in degrees. By default, it is -90 degrees. @@ -85,16 +85,21 @@ class Circle : ExtendedEntity() { if (shouldRebuildVertexBuffer) { shouldRebuildVertexBuffer = false - val segments = approximateSegments(width, height) - + val segments = approximateSegments(drawWidth, drawHeight) setVertexBuffer(CircleVertexBuffer(segments)) } - (vertexBuffer as CircleVertexBuffer).update(width, height, startAngle, endAngle) + val vertexBuffer = vertexBuffer + if (vertexBuffer is CircleVertexBuffer) { + vertexBuffer.update(drawWidth, drawHeight, startAngle, endAngle) + } } override fun drawVertices(pGL: GL10, pCamera: Camera) { - (vertexBuffer as? CircleVertexBuffer)?.draw(pGL) + val vertexBuffer = vertexBuffer + if (vertexBuffer is CircleVertexBuffer) { + vertexBuffer.draw(pGL) + } } @@ -103,7 +108,7 @@ class Circle : ExtendedEntity() { fun approximateSegments(width: Float, height: Float, maximumAngle: Float = 360f): Int { val averageRadius = (width + height) / 4f - val minSegmentAngle = min(10f, 360f / averageRadius.toRadians()) + val minSegmentAngle = min(5f, 360f / averageRadius.toRadians()) return max(3, (maximumAngle / minSegmentAngle).toInt()) } @@ -113,7 +118,7 @@ class Circle : ExtendedEntity() { } -class CircleVertexBuffer(@IntRange(from = 1) val segments: Int) : VertexBuffer( +open class CircleVertexBuffer(@IntRange(from = 1) val segments: Int) : VertexBuffer( // Explanation: Segments + 2 because the first vertex that is the center of the circle is not included in the segment // count and we add it twice so that the last vertex connects to the first one. @@ -153,7 +158,7 @@ class CircleVertexBuffer(@IntRange(from = 1) val segments: Int) : VertexBuffer( setHardwareBufferNeedsUpdate() } - fun draw(pGL: GL10) { + open fun draw(pGL: GL10) { pGL.glDrawArrays(GL11.GL_TRIANGLE_FAN, 0, segments + 2) } diff --git a/src/com/reco1l/andengine/shape/GradientCircle.kt b/src/com/reco1l/andengine/shape/GradientCircle.kt new file mode 100644 index 000000000..388651741 --- /dev/null +++ b/src/com/reco1l/andengine/shape/GradientCircle.kt @@ -0,0 +1,139 @@ +package com.reco1l.andengine.shape + +import androidx.annotation.IntRange +import com.reco1l.framework.ColorARGB +import com.reco1l.framework.Colors +import java.nio.ByteBuffer +import java.nio.ByteOrder +import java.nio.FloatBuffer +import javax.microedition.khronos.opengles.* + + +/** + * A circle that supports gradients. + * + * The gradient is applied from [startAngle] to [endAngle]. + * + * @author Reco1l + */ +class GradientCircle : Circle() { + + + /** + * The color at the start angle. + */ + var startColor: ColorARGB = ColorARGB.Black + set(value) { + if (field != value) { + field = value + updateVertexBuffer() + } + } + + /** + * The color at the end angle. + */ + var endColor = ColorARGB.White + set(value) { + if (field != value) { + field = value + updateVertexBuffer() + } + } + + private var shouldRebuildVertexBuffer = true + + + override fun setAlpha(pAlpha: Float) { + if (mAlpha != pAlpha) { + super.setAlpha(pAlpha) + updateVertexBuffer() + } + } + + override fun onUpdateVertexBuffer() { + + if (shouldRebuildVertexBuffer) { + shouldRebuildVertexBuffer = false + + val segments = approximateSegments(drawWidth, drawHeight) + setVertexBuffer(GradientCircleVertexBuffer(segments)) + } + + // Getting inherited alpha. + var alpha = mAlpha + var parent = parent + while (parent != null) { + alpha *= parent.alpha + parent = parent.parent + } + + (vertexBuffer as GradientCircleVertexBuffer).update( + drawWidth, + drawHeight, + startAngle, + endAngle, + startColor, + endColor, + alpha + ) + } +} + + +class GradientCircleVertexBuffer(@IntRange(from = 1) segments: Int) : CircleVertexBuffer(segments) { + + /** + * The buffer that holds the color data. + */ + val colorBuffer: FloatBuffer = ByteBuffer.allocateDirect((segments + 2) * 4 * Float.SIZE_BYTES) + .order(ByteOrder.nativeOrder()) + .asFloatBuffer() + + + fun update( + width: Float, + height: Float, + startAngle: Float, + endAngle: Float, + startColor: ColorARGB, + endColor: ColorARGB, + alpha: Float + ) { + super.update(width, height, startAngle, endAngle) + + colorBuffer.put(0, startColor.red) + colorBuffer.put(1, startColor.green) + colorBuffer.put(2, startColor.blue) + colorBuffer.put(3, alpha) + + val halfSegments = segments / 2 + + // The first vertex is the center of the circle. + for (i in 1..segments + 1) { + + var j = i - 1f + if (j > halfSegments) { + j = halfSegments - (j - halfSegments) + } + + val color = Colors.interpolate(j, startColor, endColor, 0f, halfSegments.toFloat()) + + colorBuffer.put(i * 4 + 0, color.red) + colorBuffer.put(i * 4 + 1, color.green) + colorBuffer.put(i * 4 + 2, color.blue) + colorBuffer.put(i * 4 + 3, alpha) + } + + setHardwareBufferNeedsUpdate() + } + + override fun draw(pGL: GL10) { + pGL.glEnableClientState(GL10.GL_COLOR_ARRAY) + pGL.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer) + super.draw(pGL) + pGL.glDisableClientState(GL10.GL_COLOR_ARRAY) + } + +} + diff --git a/src/com/reco1l/andengine/shape/Triangle.kt b/src/com/reco1l/andengine/shape/Triangle.kt new file mode 100644 index 000000000..eea71ad37 --- /dev/null +++ b/src/com/reco1l/andengine/shape/Triangle.kt @@ -0,0 +1,52 @@ +package com.reco1l.andengine.shape + +import com.reco1l.andengine.* +import org.anddev.andengine.engine.camera.* +import org.anddev.andengine.opengl.util.GLHelper +import org.anddev.andengine.opengl.vertex.VertexBuffer +import javax.microedition.khronos.opengles.* +import javax.microedition.khronos.opengles.GL10.* + +/** + * A triangle shape. + */ +open class Triangle : ExtendedEntity(vertexBuffer = TriangleVertexBuffer()) { + + + override fun onInitDraw(pGL: GL10) { + super.onInitDraw(pGL) + + GLHelper.disableCulling(pGL) + GLHelper.disableTextures(pGL) + GLHelper.disableTexCoordArray(pGL) + } + + + override fun onUpdateVertexBuffer() { + (vertexBuffer as TriangleVertexBuffer).update(drawWidth, drawHeight) + } + + override fun drawVertices(gl: GL10, camera: Camera) { + gl.glDrawArrays(GL_TRIANGLE_FAN, 0, 3) + } + + + class TriangleVertexBuffer : VertexBuffer(3 * 2, GL11.GL_STATIC_DRAW, false) { + + fun update(width: Float, height: Float) { + + floatBuffer.put(0, width / 2f) + floatBuffer.put(1, 0f) + + floatBuffer.put(2, 0f) + floatBuffer.put(3, height) + + floatBuffer.put(4, width) + floatBuffer.put(5, height) + + setHardwareBufferNeedsUpdate() + } + + } + +} diff --git a/src/com/reco1l/andengine/sprite/ExtendedSprite.kt b/src/com/reco1l/andengine/sprite/ExtendedSprite.kt index e00a35794..24c00b158 100644 --- a/src/com/reco1l/andengine/sprite/ExtendedSprite.kt +++ b/src/com/reco1l/andengine/sprite/ExtendedSprite.kt @@ -5,7 +5,6 @@ import com.reco1l.andengine.* import com.reco1l.andengine.shape.* import org.anddev.andengine.opengl.texture.region.* import org.anddev.andengine.opengl.util.* -import org.anddev.andengine.opengl.vertex.* import javax.microedition.khronos.opengles.* /** @@ -54,7 +53,7 @@ open class ExtendedSprite(textureRegion: TextureRegion? = null) : Box() { /** * The texture region of the sprite. */ - open var textureRegion: TextureRegion? = null + open var textureRegion: TextureRegion? = textureRegion set(value) { if (field == value) { @@ -94,8 +93,13 @@ open class ExtendedSprite(textureRegion: TextureRegion? = null) : Box() { init { - @Suppress("LeakingThis") - this.textureRegion = textureRegion + run { + textureRegion?.setTexturePosition(textureX, textureY) + textureRegion?.isFlippedVertical = flippedVertical + textureRegion?.isFlippedHorizontal = flippedHorizontal + + onContentSizeMeasured() + } } diff --git a/src/com/reco1l/andengine/text/ExtendedText.kt b/src/com/reco1l/andengine/text/ExtendedText.kt new file mode 100644 index 000000000..7c42670cf --- /dev/null +++ b/src/com/reco1l/andengine/text/ExtendedText.kt @@ -0,0 +1,159 @@ +package com.reco1l.andengine.text + +import com.reco1l.andengine.Axes +import com.reco1l.andengine.ExtendedEntity +import com.reco1l.toolkt.kotlin.fastForEachIndexed +import org.anddev.andengine.engine.camera.* +import org.anddev.andengine.opengl.font.* +import org.anddev.andengine.opengl.texture.buffer.* +import org.anddev.andengine.opengl.util.* +import org.anddev.andengine.opengl.vertex.* +import org.anddev.andengine.opengl.vertex.TextVertexBuffer.VERTICES_PER_CHARACTER +import org.anddev.andengine.util.* +import javax.microedition.khronos.opengles.* +import javax.microedition.khronos.opengles.GL11.GL_STATIC_DRAW +import kotlin.math.* + +/** + * A text entity that can be displayed on the screen. + */ +open class ExtendedText : ExtendedEntity() { + + + override var autoSizeAxes = Axes.Both + + + /** + * The text to be displayed + */ + var text: String = "" + set(value) { + if (field != value) { + field = value + + if (value.length > maximumSize) { + shouldRebuildVertexBuffer = true + shouldRebuildTextureBuffer = true + } + + updateVertexBuffer() + } + } + + /** + * The font to use for this text. + * It must be already loaded and ready to use before setting it. + */ + var font: Font? = null + set(value) { + if (field != value) { + field = value + shouldRebuildTextureBuffer = true + updateVertexBuffer() + } + } + + /** + * The horizontal alignment of the text. + */ + var horizontalAlign = HorizontalAlign.LEFT + set(value) { + if (field != value) { + field = value + updateVertexBuffer() + } + } + + + private var textureBuffer: TextTextureBuffer? = null + + private var shouldRebuildVertexBuffer = true + + private var shouldRebuildTextureBuffer = true + + private var maximumSize = 0 + + private var currentSize = 0 + + + override fun onInitDraw(pGL: GL10) { + super.onInitDraw(pGL) + GLHelper.enableTextures(pGL) + GLHelper.enableTexCoordArray(pGL) + } + + override fun drawVertices(gl: GL10, pCamera: Camera?) { + val vertexBuffer = vertexBuffer + if (vertexBuffer != null) { + gl.glDrawArrays(GL10.GL_TRIANGLES, 0, currentSize * VERTICES_PER_CHARACTER) + } + } + + override fun onApplyVertices(gl: GL10) { + + val font = font + val textureBuffer = textureBuffer + + if (font != null && textureBuffer != null) { + font.texture.bind(gl) + GLHelper.texCoordPointer(gl, textureBuffer.floatBuffer) + } + + super.onApplyVertices(gl) + } + + override fun onUpdateVertexBuffer() { + + val text = text + currentSize = text.length + + if (text.length > maximumSize) { + shouldRebuildVertexBuffer = true + shouldRebuildTextureBuffer = true + maximumSize = text.length + } + + if (shouldRebuildVertexBuffer) { + shouldRebuildVertexBuffer = false + + setVertexBuffer(TextVertexBuffer(maximumSize, horizontalAlign, GL_STATIC_DRAW, true)) + } + + if (shouldRebuildTextureBuffer) { + shouldRebuildTextureBuffer = false + + textureBuffer = TextTextureBuffer(2 * VERTICES_PER_CHARACTER * maximumSize, GL_STATIC_DRAW, true) + } + + val lines = text.split('\n').toTypedArray() + val linesWidth = IntArray(lines.size) + + var maximumLineWidth = 0 + + lines.fastForEachIndexed { i, line -> + linesWidth[i] = font!!.getStringWidth(line) + maximumLineWidth = max(maximumLineWidth, linesWidth[i]) + } + + contentWidth = maximumLineWidth.toFloat() + contentHeight = (lines.size * font!!.lineHeight + (lines.size - 1) * font!!.lineGap).toFloat() + onContentSizeMeasured() + + textureBuffer!!.update(font!!, lines) + vertexBuffer!!.update(font!!, maximumLineWidth, linesWidth, lines, horizontalAlign) + } + + + override fun getVertexBuffer(): TextVertexBuffer? { + return super.getVertexBuffer() as TextVertexBuffer? + } + + + override fun finalize() { + if (textureBuffer!!.isManaged) { + textureBuffer!!.unloadFromActiveBufferObjectManager() + } + super.finalize() + } + +} \ No newline at end of file diff --git a/src/com/reco1l/andengine/text/TextSprite.kt b/src/com/reco1l/andengine/text/TextSprite.kt new file mode 100644 index 000000000..29d2187e4 --- /dev/null +++ b/src/com/reco1l/andengine/text/TextSprite.kt @@ -0,0 +1,306 @@ +package com.reco1l.andengine.text + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.LinearGradient +import android.graphics.Paint +import android.graphics.Rect +import android.graphics.Shader +import android.graphics.Shader.TileMode +import android.graphics.Typeface +import android.opengl.GLUtils +import android.util.Log +import androidx.core.graphics.alpha +import com.reco1l.andengine.Axes +import com.reco1l.andengine.getDrawHeight +import com.reco1l.andengine.getDrawWidth +import com.reco1l.andengine.sprite.ExtendedSprite +import com.reco1l.framework.ColorARGB +import org.anddev.andengine.opengl.texture.Texture +import org.anddev.andengine.opengl.texture.TextureOptions +import org.anddev.andengine.opengl.texture.region.TextureRegion +import ru.nsu.ccfit.zuev.osu.GlobalManager +import javax.microedition.khronos.opengles.GL10 +import kotlin.math.max + +/** + * A sprite that displays text. + * + * Unlike [org.anddev.andengine.entity.text.Text], this is a sprite that pre-renders the entire text to a texture. + * It is not as efficient as [org.anddev.andengine.entity.text.Text], but it is more flexible and + * allows for more customization. + * + * It is not recommended to use this on places where the text changes frequently, as it will + * generate a new texture every time the text changes. + */ +class TextSprite : ExtendedSprite() { + + + override var autoSizeAxes = Axes.Both + + override var textureRegion: TextureRegion? = TextTextureRegion(TextTexture()) + set(_) { + Log.e("ExtendedText", "textureRegion is read-only for ExtendedText") + } + + + /** + * The text to be displayed. + */ + var text = "" + set(value) { + if (field != value) { + field = value + updateVertexBuffer() + } + } + + /** + * The color of the background of the text. + */ + var backgroundColor = ColorARGB.Transparent + set(value) { + if (field != value) { + field = value + updateVertexBuffer() + } + } + + /** + * The color of the text stroke. + */ + var strokeColor = ColorARGB.Transparent + set(value) { + if (field != value) { + field = value + updateVertexBuffer() + } + } + + /** + * The width of the text stroke. + */ + var strokeWidth = 0f + set(value) { + if (field != value) { + field = value + updateVertexBuffer() + } + } + + /** + * The size of the text in pixels. + */ + var size = 12f + get() { + if (relativeSize && parent != null) { + return field * max(parent.getDrawWidth(), parent.getDrawHeight()) + } + return field + } + set(value) { + if (field != value) { + field = value + updateVertexBuffer() + } + } + + /** + * Whether the size of the text is relative to the size of the parent. + */ + var relativeSize = false + set(value) { + if (field != value) { + field = value + updateVertexBuffer() + } + } + + /** + * The typeface of the text. + */ + var typeFace = Typeface.DEFAULT + set(value) { + if (field != value) { + field = value + updateVertexBuffer() + } + } + + /** + * The style of the text. + */ + var typeStyle = Typeface.NORMAL + set(value) { + if (field != value) { + field = value + updateVertexBuffer() + } + } + + + private var shader: Shader? = null + + + private val texture = textureRegion!!.texture as TextTexture + + + init { + GlobalManager.getInstance().engine.textureManager.loadTexture(texture) + } + + + override fun onUpdateVertexBuffer() { + + (textureRegion as TextTextureRegion).update( + text, + color, + backgroundColor, + strokeColor, + strokeWidth, + size, + Typeface.create(typeFace, typeStyle), + shader + ) + + onContentSizeMeasured() + super.onUpdateVertexBuffer() + } + + + // Shaders + + /** + * Sets a linear gradient color to the text. + * + * The gradient is defined by two points (x0, y0) and (x1, y1) in the range [0, 1] and two colors (startColor, endColor). + */ + fun setLinearGradient(x0: Float, y0: Float, x1: Float, y1: Float, startColor: ColorARGB, endColor: ColorARGB, tileMode: TileMode = TileMode.MIRROR) { + shader = LinearGradient( + x0 * drawWidth, + y0 * drawHeight, + x1 * drawWidth, + y1 * drawHeight, + startColor.toInt(), + endColor.toInt(), + tileMode + ) + updateVertexBuffer() + } + + + override fun finalize() { + GlobalManager.getInstance().engine.textureManager.unloadTexture(texture) + super.finalize() + } + +} + +class TextTexture : Texture(PixelFormat.RGBA_8888, TextureOptions.BILINEAR_PREMULTIPLYALPHA, null) { + + + private val paint = Paint() + + private val backgroundPaint = Paint() + + private val strokePaint = Paint() + + private val bounds = Rect() + + private val canvas = Canvas() + + + private var text: String = "" + + + fun update( + newText: String, + color: ColorARGB, + backgroundColor: ColorARGB, + strokeColor: ColorARGB, + strokeWidth: Float, + size: Float, + typeFace: Typeface, + shader: Shader? + ) { + paint.isAntiAlias = true + paint.color = color.toInt() + paint.shader = null + paint.shader = shader + paint.textSize = size + paint.typeface = typeFace + + backgroundPaint.style = Paint.Style.FILL + backgroundPaint.color = backgroundColor.toInt() + + strokePaint.style = Paint.Style.STROKE + strokePaint.color = strokeColor.toInt() + strokePaint.isAntiAlias = true + strokePaint.textSize = size + strokePaint.typeface = typeFace + strokePaint.strokeWidth = strokeWidth + + paint.getTextBounds(newText, 0, newText.length, bounds) + + text = newText + isUpdateOnHardwareNeeded = true + } + + + override fun writeTextureToHardware(pGL: GL10) { + + if (width == 0 || height == 0) { + return + } + + val bitmap = Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888) + canvas.setBitmap(bitmap) + + val offsetX = -bounds.left.toFloat() + val offsetY = -bounds.top.toFloat() + + if (backgroundPaint.color.alpha > 0f) { + canvas.drawRect(0f, 0f, bounds.width().toFloat(), bounds.height().toFloat(), backgroundPaint) + } + + canvas.drawText(text, offsetX, offsetY, paint) + + if (strokePaint.strokeWidth > 0 && strokePaint.color.alpha > 0) { + canvas.drawText(text, offsetX, offsetY, strokePaint) + } + + GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0) + bitmap.recycle() + } + + + override fun getWidth(): Int { + return bounds.width() + } + + override fun getHeight(): Int { + return bounds.height() + } + +} + +class TextTextureRegion(private val textTexture: TextTexture): TextureRegion(textTexture, 0, 0, textTexture.width, textTexture.height) { + + + fun update( + text: String, + color: ColorARGB, + backgroundColor: ColorARGB, + strokeColor: ColorARGB, + strokeWidth: Float, + size: Float, + typeFace: Typeface, + shader: Shader? + ) { + textTexture.update(text, color, backgroundColor, strokeColor, strokeWidth, size, typeFace, shader) + mWidth = textTexture.width + mHeight = textTexture.height + updateTextureRegionBuffer() + } + +} \ No newline at end of file diff --git a/src/com/reco1l/framework/ColorComponent.kt b/src/com/reco1l/framework/ColorComponent.kt index f4b33c776..b89c00b0b 100644 --- a/src/com/reco1l/framework/ColorComponent.kt +++ b/src/com/reco1l/framework/ColorComponent.kt @@ -5,6 +5,8 @@ import android.graphics.* @JvmInline value class ColorARGB(private val hex: Int) { + constructor(hex: Long): this(hex.toInt()) + @JvmOverloads constructor(red: Int, green: Int, blue: Int, alpha: Int = 255): this(Color.argb(alpha, red, green, blue)) diff --git a/src/com/reco1l/framework/math/Vec2.kt b/src/com/reco1l/framework/math/Vec2.kt new file mode 100644 index 000000000..283e30167 --- /dev/null +++ b/src/com/reco1l/framework/math/Vec2.kt @@ -0,0 +1,57 @@ +package com.reco1l.framework.math + +data class Vec2( + + val x: Float, + + val y: Float, + +) { + + constructor(value: Float = 0f) : this(value, value) + + + val total + get() = x + y + + val vertical + get() = y + + val horizontal + get() = x + + + operator fun plus(other: Vec2) = Vec2( + x + other.x, + y + other.y + ) + + operator fun minus(other: Vec2) = Vec2( + x - other.x, + y - other.y + ) + + operator fun times(scalar: Float) = Vec2( + x * scalar, + y * scalar, + ) + + operator fun div(scalar: Float) = Vec2( + x / scalar, + y / scalar + ) + + operator fun unaryMinus() = Vec2( + -x, + -y + ) + + override fun toString() = "Vector2($x, $y)" + + + companion object { + val Zero = Vec2() + val One = Vec2(1f, 1f) + } + +} \ No newline at end of file diff --git a/src/com/reco1l/framework/math/Vec4.kt b/src/com/reco1l/framework/math/Vec4.kt new file mode 100644 index 000000000..a4b11dade --- /dev/null +++ b/src/com/reco1l/framework/math/Vec4.kt @@ -0,0 +1,83 @@ +package com.reco1l.framework.math + +data class Vec4( + + val x: Float, + val y: Float, + val z: Float, + val w: Float, + +) { + + constructor(value: Float = 0f) : this(value, value, value, value) + + constructor(xz: Float, yw: Float) : this(xz, yw, xz, yw) + + + val left: Float + get() = x + + val top: Float + get() = y + + val right: Float + get() = z + + val bottom: Float + get() = w + + + val total + get() = x + y + z + w + + val vertical + get() = y + w + + val horizontal + get() = x + z + + + operator fun plus(other: Vec4) = Vec4( + x + other.x, + y + other.y, + z + other.z, + w + other.w + ) + + operator fun minus(other: Vec4) = Vec4( + x - other.x, + y - other.y, + z - other.z, + w - other.w + ) + + operator fun times(scalar: Float) = Vec4( + x * scalar, + y * scalar, + z * scalar, + w * scalar + ) + + operator fun div(scalar: Float) = Vec4( + x / scalar, + y / scalar, + z / scalar, + w / scalar + ) + + operator fun unaryMinus() = Vec4( + -x, + -y, + -z, + -w + ) + + override fun toString() = "Vector4($x, $y, $z, $w)" + + + companion object { + val Zero = Vec4() + val One = Vec4(1f, 1f, 1f, 1f) + } + +} \ No newline at end of file diff --git a/src/com/reco1l/osu/hitobjects/FollowPoints.kt b/src/com/reco1l/osu/hitobjects/FollowPoints.kt index 81d604db1..b604d6666 100644 --- a/src/com/reco1l/osu/hitobjects/FollowPoints.kt +++ b/src/com/reco1l/osu/hitobjects/FollowPoints.kt @@ -117,8 +117,8 @@ object FollowPointConnection { fp.clearEntityModifiers() fp.setPosition(pointStartX, pointStartY) - fp.setOrigin(Anchor.Center) fp.setScale(1.5f * scale) + fp.origin = Anchor.Center fp.rotation = rotation fp.alpha = 0f diff --git a/src/com/reco1l/osu/hitobjects/SliderTicks.kt b/src/com/reco1l/osu/hitobjects/SliderTicks.kt index a1d2a6faf..c9d006cbd 100644 --- a/src/com/reco1l/osu/hitobjects/SliderTicks.kt +++ b/src/com/reco1l/osu/hitobjects/SliderTicks.kt @@ -77,7 +77,7 @@ class SliderTickSprite : ExtendedSprite() { init { textureRegion = ResourceManager.getInstance().getTexture("sliderscorepoint") - setOrigin(Anchor.Center) + origin = Anchor.Center } /** diff --git a/src/com/reco1l/osu/playfield/CirclePiece.kt b/src/com/reco1l/osu/playfield/CirclePiece.kt index b58a6d4c2..85a6a17d7 100644 --- a/src/com/reco1l/osu/playfield/CirclePiece.kt +++ b/src/com/reco1l/osu/playfield/CirclePiece.kt @@ -3,6 +3,7 @@ package com.reco1l.osu.playfield import com.reco1l.andengine.* import com.reco1l.andengine.container.* import com.reco1l.andengine.sprite.* +import com.rian.osu.math.Vector2 import ru.nsu.ccfit.zuev.osu.* import ru.nsu.ccfit.zuev.skins.* @@ -14,15 +15,15 @@ open class CirclePiece( ) : Container() { - override var originX = 0.5f - - override var originY = 0.5f + init { + origin = Anchor.Center + } private val circle = ExtendedSprite().also { - it.setOrigin(Anchor.Center) - it.setAnchor(Anchor.Center) + it.origin = Anchor.Center + it.anchor = Anchor.Center it.textureRegion = ResourceManager.getInstance().getTexture(circleTexture) attachChild(it) @@ -30,8 +31,8 @@ open class CirclePiece( private val overlay = ExtendedSprite().also { - it.setOrigin(Anchor.Center) - it.setAnchor(Anchor.Center) + it.origin = Anchor.Center + it.anchor = Anchor.Center it.textureRegion = ResourceManager.getInstance().getTexture(overlayTexture) attachChild(it) @@ -48,8 +49,8 @@ class NumberedCirclePiece(circleTexture: String, overlayTexture: String) : Circl private val number = SpriteFont(OsuSkin.get().hitCirclePrefix).also { - it.setOrigin(Anchor.Center) - it.setAnchor(Anchor.Center) + it.origin = Anchor.Center + it.anchor = Anchor.Center it.spacing = -OsuSkin.get().hitCircleOverlap attachChild(it) diff --git a/src/com/reco1l/osu/playfield/CircularSongProgress.kt b/src/com/reco1l/osu/playfield/CircularSongProgress.kt index 1437aa6ce..1da0373e3 100644 --- a/src/com/reco1l/osu/playfield/CircularSongProgress.kt +++ b/src/com/reco1l/osu/playfield/CircularSongProgress.kt @@ -21,11 +21,10 @@ class CircularSongProgress : Container() { Circle().also { clear -> clear.setSize(30f, 30f) - clear.setAnchor(Anchor.Center) - clear.setOrigin(Anchor.Center) + clear.anchor = Anchor.Center + clear.origin = Anchor.Center clear.color = ColorARGB.Transparent - clear.testWithDepthBuffer = true - clear.clearDepthBufferBeforeDraw = true + clear.depthInfo = DepthInfo.Clear attachChild(clear) } @@ -33,10 +32,10 @@ class CircularSongProgress : Container() { Circle().also { background -> background.setSize(33f, 33f) - background.setAnchor(Anchor.Center) - background.setOrigin(Anchor.Center) + background.anchor = Anchor.Center + background.origin = Anchor.Center background.color = ColorARGB.White - background.testWithDepthBuffer = true + background.depthInfo = DepthInfo.Default attachChild(background) } @@ -44,8 +43,8 @@ class CircularSongProgress : Container() { circularProgress = Circle().also { progress -> progress.setSize(30f, 30f) - progress.setAnchor(Anchor.Center) - progress.setOrigin(Anchor.Center) + progress.anchor = Anchor.Center + progress.origin = Anchor.Center progress.alpha = 0.6f attachChild(progress) @@ -55,8 +54,8 @@ class CircularSongProgress : Container() { Circle().also { dot -> dot.setSize(4f, 4f) - dot.setAnchor(Anchor.Center) - dot.setOrigin(Anchor.Center) + dot.anchor = Anchor.Center + dot.origin = Anchor.Center dot.color = ColorARGB.White attachChild(dot) @@ -64,8 +63,8 @@ class CircularSongProgress : Container() { onMeasureContentSize() - setAnchor(Anchor.TopRight) - setOrigin(Anchor.CenterRight) + anchor = Anchor.TopRight + origin = Anchor.CenterRight } diff --git a/src/com/reco1l/osu/playfield/Counters.kt b/src/com/reco1l/osu/playfield/Counters.kt index 25bee82d8..aa3aa380a 100644 --- a/src/com/reco1l/osu/playfield/Counters.kt +++ b/src/com/reco1l/osu/playfield/Counters.kt @@ -17,8 +17,8 @@ class ScoreCounter : SpriteFont(OsuSkin.get().scorePrefix) { init { - setAnchor(Anchor.TopRight) - setOrigin(Anchor.TopRight) + anchor = Anchor.TopRight + origin = Anchor.TopRight setScale(0.96f) x = -10f @@ -35,8 +35,8 @@ class ScoreCounter : SpriteFont(OsuSkin.get().scorePrefix) { class PPCounter(private val algorithm: DifficultyAlgorithm) : SpriteFont(OsuSkin.get().scorePrefix) { init { - setAnchor(Anchor.TopRight) - setOrigin(Anchor.TopRight) + anchor = Anchor.TopRight + origin = Anchor.TopRight setScale(0.6f * 0.96f) setValue(0.0) } @@ -55,8 +55,8 @@ class AccuracyCounter : SpriteFont(OsuSkin.get().scorePrefix) { init { - setAnchor(Anchor.TopRight) - setOrigin(Anchor.TopRight) + anchor = Anchor.TopRight + origin = Anchor.TopRight setScale(0.6f * 0.96f) setPosition(-17f, 9f) text = "100.00%" @@ -77,14 +77,14 @@ class ComboCounter : Container() { it.alpha = 0f it.text = "0x" - it.setAnchor(Anchor.BottomLeft) - it.setOrigin(Anchor.BottomLeft) + it.anchor = Anchor.BottomLeft + it.origin = Anchor.BottomLeft // In stable, the bigger pop out scales a bit to the left it.translationX = -3f - it.translationY = -(FONT_HEIGHT_RATIO * it.height + VERTICAL_OFFSET) + it.translationY = -(FONT_HEIGHT_RATIO * it.drawHeight + VERTICAL_OFFSET) - it.y = -(1 - FONT_HEIGHT_RATIO) * it.height + VERTICAL_OFFSET + it.y = -(1 - FONT_HEIGHT_RATIO) * it.drawHeight + VERTICAL_OFFSET it.spacing = -OsuSkin.get().comboOverlap attachChild(it) @@ -94,12 +94,12 @@ class ComboCounter : Container() { private val displayedCountTextSprite = SpriteFont(OsuSkin.get().comboPrefix).also { it.text = "0x" - it.setAnchor(Anchor.BottomLeft) - it.setOrigin(Anchor.BottomLeft) + it.anchor = Anchor.BottomLeft + it.origin = Anchor.BottomLeft - it.translationY = -(FONT_HEIGHT_RATIO * it.height + VERTICAL_OFFSET) + it.translationY = -(FONT_HEIGHT_RATIO * it.drawHeight + VERTICAL_OFFSET) - it.y = -(1 - FONT_HEIGHT_RATIO) * it.height + VERTICAL_OFFSET + it.y = -(1 - FONT_HEIGHT_RATIO) * it.drawHeight + VERTICAL_OFFSET it.spacing = -OsuSkin.get().comboOverlap attachChild(it, 0) @@ -114,8 +114,8 @@ class ComboCounter : Container() { init { - setAnchor(Anchor.BottomLeft) - setOrigin(Anchor.BottomLeft) + anchor = Anchor.BottomLeft + origin = Anchor.BottomLeft setPosition(10f, -10f) setScale(1.28f) } diff --git a/src/com/reco1l/osu/playfield/GameplayHUD.kt b/src/com/reco1l/osu/playfield/GameplayHUD.kt index 0d63e04c5..886729d2c 100644 --- a/src/com/reco1l/osu/playfield/GameplayHUD.kt +++ b/src/com/reco1l/osu/playfield/GameplayHUD.kt @@ -74,7 +74,7 @@ class GameplayHUD(private val stat: StatisticV2, private val game: GameScene, pr scoreCounter!!.setScore(stat.totalScoreWithMultiplier) accuracyCounter!!.setAccuracy(stat.accuracy) - accuracyCounter.y = 9f + scoreCounter.y + scoreCounter.height + accuracyCounter.y = 9f + scoreCounter.y + scoreCounter.drawHeight if (Config.getProgressIndicatorType() == PIE) { pieSongProgress!!.x = accuracyCounter.x - accuracyCounter.widthScaled - 18f diff --git a/src/com/reco1l/osu/playfield/HealthBar.kt b/src/com/reco1l/osu/playfield/HealthBar.kt index 6e910c33e..a764d516d 100644 --- a/src/com/reco1l/osu/playfield/HealthBar.kt +++ b/src/com/reco1l/osu/playfield/HealthBar.kt @@ -49,24 +49,24 @@ class HealthBar(private val statistics: StatisticV2) : Container() { attachChild(ExtendedSprite().apply { textureRegion = backgroundTexture }) fillClear = Box() - fillClear.setOrigin(Anchor.TopRight) - fillClear.clearDepthBufferBeforeDraw = true - fillClear.testWithDepthBuffer = true + fillClear.origin = Anchor.TopRight + fillClear.depthInfo = DepthInfo.Clear fillClear.alpha = 0f attachChild(fillClear) fill = AnimatedSprite("scorebar-colour", true, OsuSkin.get().animationFramerate) - fill.testWithDepthBuffer = true + fill.depthInfo = DepthInfo.Default fill.autoSizeAxes = Axes.None // Preserve the first frame width. attachChild(fill) marker = ExtendedSprite() - marker.setOrigin(Anchor.Center) + marker.origin = Anchor.Center + marker.blendInfo = BlendInfo(BlendingFunction.Additive) attachChild(marker) explode = ExtendedSprite() - explode.setOrigin(Anchor.Center) - explode.blendingFunction = BlendingFunction.Additive + explode.origin = Anchor.Center + explode.blendInfo = BlendInfo(BlendingFunction.Additive) explode.alpha = 0f attachChild(explode) @@ -91,17 +91,17 @@ class HealthBar(private val statistics: StatisticV2) : Container() { } fillClear.width = 0f - fillClear.height = fill.height - fillClear.setPosition(fill.x + fill.width, fill.y) + fillClear.height = fill.drawHeight + fillClear.setPosition(fill.x + fill.drawWidth, fill.y) } override fun onManagedUpdate(pSecondsElapsed: Float) { - fillClear.width = Interpolation.floatAt(pSecondsElapsed.coerceIn(0f, 0.2f), fillClear.width, (1f - statistics.hp) * fill.width, 0f, 0.2f, Easing.OutQuint) + fillClear.width = Interpolation.floatAt(pSecondsElapsed.coerceIn(0f, 0.2f), fillClear.drawWidth, (1f - statistics.hp) * fill.drawWidth, 0f, 0.2f, Easing.OutQuint) - marker.x = fill.x + fill.width - fillClear.width - marker.y = fill.y + (if (isNewStyle) fill.height / 2 else 0f) + marker.x = fill.x + fill.drawWidth - fillClear.drawWidth + marker.y = fill.y + (if (isNewStyle) fill.drawHeight / 2 else 0f) explode.setPosition(marker) @@ -117,7 +117,7 @@ class HealthBar(private val statistics: StatisticV2) : Container() { fill.color = color marker.color = color - marker.blendingFunction = if (statistics.hp < EPIC_CUTOFF) BlendingFunction.Inherit else BlendingFunction.Additive + marker.blendInfo?.function = if (statistics.hp < EPIC_CUTOFF) BlendingFunction.Inherit else BlendingFunction.Additive } else { @@ -140,7 +140,7 @@ class HealthBar(private val statistics: StatisticV2) : Container() { bulge() explode.clearEntityModifiers() - explode.blendingFunction = if (isEpic) BlendingFunction.Additive else BlendingFunction.Inherit + explode.blendInfo?.function = if (isEpic) BlendingFunction.Additive else BlendingFunction.Inherit explode.alpha = 1f explode.setScale(1f) diff --git a/src/ru/nsu/ccfit/zuev/osu/game/GameplaySpinner.java b/src/ru/nsu/ccfit/zuev/osu/game/GameplaySpinner.java index 0bf48c3ee..6ad1fd6b1 100644 --- a/src/ru/nsu/ccfit/zuev/osu/game/GameplaySpinner.java +++ b/src/ru/nsu/ccfit/zuev/osu/game/GameplaySpinner.java @@ -64,7 +64,7 @@ public GameplaySpinner() { background.setOrigin(Anchor.Center); background.setPosition(center.x, center.y); background.setTextureRegion(ResourceManager.getInstance().getTexture("spinner-background")); - background.setScale(Config.getRES_WIDTH() / background.getWidth()); + background.setScale(Config.getRES_WIDTH() / background.getDrawWidth()); circle = new ExtendedSprite(); circle.setOrigin(Anchor.Center);