Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Camera doesn't follow entity after calling camera.anchor #596

Open
SunShiranui opened this issue Jun 16, 2018 · 8 comments
Open

Camera doesn't follow entity after calling camera.anchor #596

SunShiranui opened this issue Jun 16, 2018 · 8 comments

Comments

@SunShiranui
Copy link

  • HaxePunk version: git
  • Target(s): neko
  • Haxe version: 3.4.7
  • OpenFL/Lime or NME version: openfl 8.2.2, lime 6.4.0

After upgrading from HaxePunk 4.0.1 to the current dev version, the camera doesn't follow the entity it's been anchored to.

Previously, I used this code in my Scene's begin function:

override public function begin()
{
[...]
	camera.anchor(player);
[...]
}

The camera used to follow the player entity correctly, but now it's only moved once (when the Scene starts).

@kro13
Copy link

kro13 commented Jul 5, 2018

the problem is in that Camera.anchor accepts Vector2 as an argument, which is an abstract over Position, which in it's turn creates a NEW Vector2 and the reference is lost

@XANOZOID
Copy link
Member

So, @SunShiranui , to follow up with what @kro13 said, you'll need to call this function in an update since it does not have an update of its own.

This should be closed.

@SunShiranui
Copy link
Author

Looking at the source code, the function doesn't seem to be intended to be used that way, so I would still classify this as a bug.

@XANOZOID
Copy link
Member

XANOZOID commented Dec 17, 2018

Thanks for pointing that out! Definitely seems to assume that Vector2 is some form of an Entity when, like kro13 said, Vector2 is an abstract that converts an Entity into a different structure entirely during compile time.

A few solutions come to mind. Camera could be changed to accept any instance which has an X/Y, that way the reference isn't lost . . . or we could change Vector2 to an interface since that's what Camera assumes . . . or we could make an assumption, based on the current implementation, that anchor is ultimately looking for an Entity and save the actual entity reference; if it's not an entity, but an element comparable to the TypeDef of Vector2, then we'll update the camera's position on-call and nullify the reference.

@XANOZOID
Copy link
Member

XANOZOID commented Dec 23, 2018

Well, color me confused I tested this code out and it worked like it should have without changing haxepunk at all . . .

package scenes;

import haxepunk.Scene;
import haxepunk.Entity;
import haxepunk.graphics.Image;
import haxepunk.utils.Color;

class GarbageScene extends Scene {


    public var mouse:Entity;

    override public function begin() {

        mouse = new Entity( x, y );
        mouse.graphic = Image.createRect( 32, 32, Color.White );
        mouse.graphic.centerOrigin();
        add( mouse );

        camera.anchor( mouse );
        
    }

    override public function update(){

        // mouse should always be in center of screen since camera is anchored . . . 
        mouse.moveTo( mouseX, mouseY );
        trace( camera.x );
        
        super.update();
    }

}

So, are we sure it's a HaxePunk problem, or a Haxe 4.0 problem? HaxePunk doesn't currently support Haxe 4.0 but it is definitely something to consider when Haxe 4.0 is a supported version. . . I have a feeling this break in code is a result of something being broken in the 4.0 preview or a direct change of language features . . .

Can you also change the HaxePunk version from 3.4.7 to the 4.preview you're using in the main post? Thanks!

--- edit again:

Going to double check and make sure my HaxePunk version isn't the difference in this case . . .

Okay, so after looking at the Vector2 in my version there's definitely a difference from the git version. I'm now trying to reason about why they even decided to steer away from the original "class" based implementation they had to a lossy "abstraction". I imagine the abstract-based implementation for the git version doesn't actually have many, if any, real improvements over the one I'm using.

@SunShiranui
Copy link
Author

I can confirm I'm using Haxe 3.4.7. Have you managed to reproduce the bug using HaxePunk git version?
I'm guessing the regression is a result of this commit, but haven't been able to look into it: Merged Position and Vector2 (#580)

@XANOZOID
Copy link
Member

XANOZOID commented Dec 23, 2018

I've created a test for the camera anchor which I've recently submitted. It shows that the git version is failing, like you have found.

Once that gets accepted I'll work on a fix. I'm debating whether or not to change the Vector2 implementation or the Camera implementation. Being that I haven't read other reports involving Vector2, so far, I'm more focused on fixing the anchor functionality - and I know what I can do for that.

I'll also take a look at the commit you mentioned and see if there's anything that is more obvious to do than what I'm thinking of.

Edit:
That commit definitely seems to show the change from class to a typedef implementation. I'll still focus on the Camera anchor for now, and then discuss if we should consider the problems associated with the new Vector2 implementation with Matrefeytontias.

@XANOZOID
Copy link
Member

XANOZOID commented Dec 27, 2018

I ended up creating a version that wouldn't add any extra data definitions that also only effected Camera-hx. Although it did exactly what I thought it should have done, I recently observed that the original implementation never assumed to only update with an entity. So the newest version I have come up with is faulty and I am coming to some new approaches.

I'm thinking the solution may just end up having to be creating some new data types or modifying the Vector2 somehow. I prefer not introducing new data types just for the anchor problem, but messing with Vector2 means touching a lot of code. Luckily HaxePunk has unit tests, so if anything goes wrong it will probably be caught and if not new tests will be added.

This is the incorrect fix:

package haxepunk;

import haxepunk.math.Vector2;

/**
 * @since 4.0.0
 */
class Camera
{
	public var x:Float = 0;
	public var y:Float = 0;

	public var scale:Float = 1;
	public var scaleX:Float = 1;
	public var scaleY:Float = 1;

	public function new(x:Float = 0, y:Float = 0)
	{
		this.x = x;
		this.y = y;
	}

	/**
	 * Set the Camera's position. If provided, px and py determine the part of
	 * the screen to move to the given position; 0.5 will center the camera,
	 * and 1.0 will set the right edge.
	 */
	public inline function setTo(x:Float, y:Float, px:Float = 0, py:Float = 0)
	{
		this.x = x - ((HXP.width / fullScaleX) * px);
		this.y = y - ((HXP.height / fullScaleY) * py);
	}

	/**
	 * Whether this graphic will be snapped to the nearest whole number pixel
	 * position when rendering. If this is true for either an individual
	 * Graphic or for the Camera, snapping will occur.
	 */
	public var pixelSnapping:Bool = false;

	public var fullScaleX(get, never):Float;
	inline function get_fullScaleX() return scale * scaleX;
	public var fullScaleY(get, never):Float;
	inline function get_fullScaleY() return scale * scaleY;

	public var screenScaleX(get, never):Float;
	inline function get_screenScaleX() return fullScaleX * HXP.screen.scaleX;
	public var screenScaleY(get, never):Float;
	inline function get_screenScaleY() return fullScaleY * HXP.screen.scaleY;

	public var width(get, never):Float;
	inline function get_width() return HXP.screen.width / screenScaleX;

	public var height(get, never):Float;
	inline function get_height() return HXP.screen.height / screenScaleY;

	/**
	 * Return an X value that, after scaling, will result in an integer.
	 */
	public inline function floorX(x:Float) return Math.floor((x + 0.5) * screenScaleX) / screenScaleX;
	/**
	 * Return a Y value that, after scaling, will result in an integer.
	 */
	public inline function floorY(y:Float) return Math.floor((y + 0.5) * screenScaleY) / screenScaleY;

	var anchorTarget:Null<Entity>;
	var anchorX:Float = 0;
	var anchorY:Float = 0;


	/**
	 * Anchor the Camera to an Entity or other object with position. The
	 * Camera will keep the target in the specified part of the screen.
	 * @since 4.0.0
	 * @param target An entity is permanently followed, other instances are anchored once when called.
	 * @param anchorX x offset for the camera anchor from the instance.
	 * @param anchorY y offset for the camera anchor from the instance.
	 */
	public function anchor(?targetE:Entity, ?target:Vector2, anchorX:Float = 0.5, anchorY:Float = 0.5)
	{
		this.anchorX = anchorX;
		this.anchorY = anchorY;
		anchorTarget = null;

		if (targetE != null)
		{
			anchorTarget = targetE;
			anchorPosition(targetE.centerX, targetE.centerY);
		} 
		else if (target != null)
		{
			anchorPosition(target.x, target.y);
		}
	}

	public function onCamera(entity:Entity):Bool
	{
		return entity.collideRect(entity.x, entity.y, x, y, HXP.width, HXP.height);
	}

	/**
	 * Cause the screen to shake for a specified length of time.
	 * @param	duration	Duration of shake effect, in seconds.
	 * @param	magnitude	Number of pixels to shake in any direction.
	 * @since	2.5.3
	 */
	public function shake(duration:Float = 0.5, magnitude:Int = 4)
	{
		if (_shakeTime < duration) _shakeTime = duration;
		_shakeMagnitude = magnitude;
	}

	/**
	 * Stop the screen from shaking immediately.
	 * @since	2.5.3
	 */
	public function shakeStop()
	{
		_shakeTime = 0;
	}

	public function update()
	{
		if (anchorTarget != null) anchorPosition(anchorTarget.centerX, anchorTarget.centerY);

		// screen shake
		if (_shakeTime > 0)
		{
			var sx:Int = Std.random(_shakeMagnitude * 2 + 1) - _shakeMagnitude;
			var sy:Int = Std.random(_shakeMagnitude * 2 + 1) - _shakeMagnitude;

			x += sx - _shakeX;
			y += sy - _shakeY;

			_shakeX = sx;
			_shakeY = sy;

			_shakeTime -= HXP.elapsed;
			if (_shakeTime < 0) _shakeTime = 0;
		}
		else if (_shakeX != 0 || _shakeY != 0)
		{
			x -= _shakeX;
			y -= _shakeY;
			_shakeX = _shakeY = 0;
		}
	}

	private inline function anchorPosition(tx:Float, ty:Float)
	{
		x = tx - (HXP.width / fullScaleX * anchorX);
		y = ty - (HXP.height / fullScaleY * anchorY);
	}

	var _shakeTime:Float=0;
	var _shakeMagnitude:Int=0;
	var _shakeX:Int=0;
	var _shakeY:Int=0;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants