An arrow that always point to the player (Solved)

Post here for help and support regarding LunaLua and SMBX2's libraries and features.
cato
Volcano Lotus
Volcano Lotus
Posts: 547
Joined: Thu Aug 24, 2017 3:06 am
Flair: Koishi enjoyer
Contact:

An arrow that always point to the player (Solved)

Postby cato » Thu Dec 08, 2022 11:16 pm

I am trying to make an arrow that always point to the player...well it is a simpler problem than my initial one. Just a Rinka that spins before moving, then aims at the player with the sprite in the correct direction. It wasn't as easy as finding the distance between the NPC and the player, the direction to the player then the angle to rotate the whole sprite. What I did is look at Rinka code and add something to it in related to rotating the sprite. It did work, but not very accurately. The angle scales weirdly (rotating less when the angle is larger) and caps at 75 deg (or whatever I already tested with which quadrant each belongs in).

Here's the code:
Loading the sprite:
Spoiler: show

Code: Select all

local iniNPC = function(v)
	local config = NPC.config[v.id]
	if not v.data.ini then
	  	v.data.ini = true
		v.data.sprite = Sprite.box{
			x = v.x,
			y = v.y,
			width = npcutils.gfxwidth(v),
			height = npcutils.gfxheight(v),
			texture = Graphics.sprites.npc[v.id].img,
			rotation = 0,
			align = Sprite.align.CENTRE,
			frames = npcutils.getTotalFramesByFramestyle(v)
		}
	end
end
Register events:
Spoiler: show

Code: Select all

function arrow.onInitAPI()
	npcManager.registerEvent(npcID, arrow, "onTickNPC")         --this is only for text debugging
	npcManager.registerEvent(npcID, arrow, "onTickEndNPC")
	npcManager.registerEvent(npcID, arrow, "onDrawNPC")       --this is for drawing the sprite
end

local temp = 0
local angle = 0
local quadrent = 0
local PosX = 0
local PosY = 0

function arrow.onTickEndNPC(v)
	--Don't act during time freeze
	if Defines.levelFreeze then return end
	if v:mem(0x138, FIELD_WORD) > 0 then return end
	
	local data = v.data
	
	--If despawned
	if v.despawnTimer <= 0 then
		--Reset our properties, if necessary
		data.animationTimer = 0
                data.animationDirection = 1
                data.frame = 0
		data.initialized = false
		return
	end

	--Initialize
	if not data.initialized then
		v.ai1 = 0
		v.ai2 = 0
		data.initialized = true
	end

	--Execute main AI.
	if not v:mem(0x136, FIELD_BOOL) and v:mem(0x12C, FIELD_WORD) == 0 and data.initialized then
		arrow.arrowSourceHoming(v, data.speed or NPC.config[v.id].speed)
	end
end
Main function
Spoiler: show

Code: Select all

function arrow.arrowSourceHoming(v, speed)
    local targetPlayer = npcutils.getNearestPlayer(v)
    local toPlayerVect = vector(targetPlayer.x - v.x, targetPlayer.y - v.y)
    local speedVect = toPlayerVect:normalise() * speed        --Problem probably here. Speed (of NPC) changes the scaling of result angles. 

    PosX = speedVect.x                                                       --But using just using toPlayerVect made it worse
    PosY = speedVect.y
 
       --this part is right as I tested myself in each quadrant 
	temp = math.floor(math.atan(PosY, PosX)*(180/math.pi))
	if PosY > 0 then
		if PosX > 0 then
			angle = math.floor(temp)
			quadrent = 1
		else angle = math.floor(180 - temp)
			quadrent = 2
		end
	else
		if PosX > 0 then
			angle = math.floor(temp)
			quadrent = 4
		else angle = math.floor(180 - temp)
			quadrent = 3
		end
	end

	--Rotate
	iniNPC(v)
	v.data.sprite.rotation = 0          --reset after each rotation so it doesn't spin crazy
	v.data.sprite:rotate(angle)
end
Drawing:
Spoiler: show

Code: Select all

function arrow.onDrawNPC(v)
	if v:mem(0x12A, FIELD_WORD) <= 0 then return end

	local config = NPC.config[v.id]
	local p = -45

	if config.foreground then	p = -15	end
	v.data.sprite.x = v.x + v.width*0.5 + config.gfxoffsetx
	v.data.sprite.y = v.y + v.height*0.5 + config.gfxoffsety
	v.data.sprite:draw{priority = p - 0.1, sceneCoords = true, frame = v.animationFrame + 1}

	npcutils.hideNPC(v)
end
I even lowered my expectations and start in a really simple way by not using vectors, but basic distance and angle measuring. Somehow, the atan() function just doesn't return the correct angle in the Text debug (still caps on 1.56rad) when compared to the calculator.
Last edited by cato on Fri Dec 09, 2022 10:12 am, edited 1 time in total.

deice
Volcano Lotus
Volcano Lotus
Posts: 538
Joined: Fri Jul 23, 2021 7:35 am

Re: An arrow that always point to the player

Postby deice » Fri Dec 09, 2022 8:02 am

using atan and checking quadrants is a little headache-inducing imo. here's a clean solution that works for me:

Code: Select all

function arrow.arrowSourceHoming(v, speed)
    local targetPlayer = npcutils.getNearestPlayer(v)
    
	local playerVect = vector.v2(targetPlayer.x + targetPlayer.width * 0.5, targetPlayer.y + targetPlayer.height * 0.5)
	local selfVect = vector.v2(v.x + v.width * 0.5, v.y + v.height * 0.5)
	
	local pointingVect = (selfVect - playerVect):normalise() * speed
	
	if(pointingVect.length == 0) then
		return
	end
	
	local normalVect = vector.v2(1, 0)
	
	local angle = math.deg(math.acos((pointingVect..normalVect) / (pointingVect.length * normalVect.length)))
	if(pointingVect.y < 0) then
		angle = -angle
	end
	
	--Rotate
	iniNPC(v)
	v.data.sprite.rotation = 0          --reset after each rotation so it doesn't spin crazy
	v.data.sprite:rotate(angle)
end
this assumes that the base sprite has the arrow pointing to the left, if it's not you should just be able to change the angle passed to the rotate call and have it work.

cato
Volcano Lotus
Volcano Lotus
Posts: 547
Joined: Thu Aug 24, 2017 3:06 am
Flair: Koishi enjoyer
Contact:

Re: An arrow that always point to the player

Postby cato » Fri Dec 09, 2022 10:11 am

deice wrote:
Fri Dec 09, 2022 8:02 am
using atan and checking quadrants is a little headache-inducing imo. here's a clean solution that works for me:
This works smoothly. I can see the equation itself being the formula of Vector Projection. That surely is way simpler and more accurate than using atan. This will be very useful for Rinka projectiles.

Thanks for your help as always.

Hoeloe
Foo
Foo
Posts: 1463
Joined: Sat Oct 03, 2015 6:18 pm
Flair: The Codehaus Girl
Pronouns: she/her

Re: An arrow that always point to the player (Solved)

Postby Hoeloe » Fri Dec 09, 2022 2:13 pm

That's not what vector projection does. Vector projection is something totally unrelated. Using atan is probably the simplest method, though I think the Sprite class might have a "lookAt" function that might work.

deice
Volcano Lotus
Volcano Lotus
Posts: 538
Joined: Fri Jul 23, 2021 7:35 am

Re: An arrow that always point to the player (Solved)

Postby deice » Fri Dec 09, 2022 3:08 pm

cato wrote: I can see the equation itself being the formula of Vector Projection.
Hoeloe wrote:
Fri Dec 09, 2022 2:13 pm
That's not what vector projection does. Vector projection is something totally unrelated.
indeed it isn't vector projection, it's just using the standard dot product formula to find the angle of the vector pointing from the npc to the player in relation to the x axis.


Return to “LunaLua Help”

Who is online

Users browsing this forum: No registered users and 0 guests

SMWCentralTalkhausMario Fan Games GalaxyKafukaMarioWikiSMBXEquipoEstelari