Page 1 of 1

An arrow that always point to the player (Solved)

Posted: Thu Dec 08, 2022 11:16 pm
by cato
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.

Re: An arrow that always point to the player

Posted: Fri Dec 09, 2022 8:02 am
by deice
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.

Re: An arrow that always point to the player

Posted: Fri Dec 09, 2022 10:11 am
by cato
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.

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

Posted: Fri Dec 09, 2022 2:13 pm
by Hoeloe
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.

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

Posted: Fri Dec 09, 2022 3:08 pm
by deice
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.