As an practice exercise (no optimization) I wrote a quick demo for this. You can copy and paste this in a new project on a Node2D's script.
extends Node2D
var tex = preload("res://icon.png")
var tex_ofs
const FOV_HALF_ANGLE = cos(deg2rad(22.5))
class Entity:
var pos
var facing_direction # Should be a unit length
var fov_edge = FOV_HALF_ANGLE
func _init(x, y, angle = 0):
pos = Vector2(x, y)
set_direction(angle)
func set_direction(angle):
facing_direction = Vector2(sin(angle), cos(angle))
func can_see(target):
var direction_to_target = (target.pos - self.pos).normalized()
return direction_to_target.dot(facing_direction) >= fov_edge
var center_canvas
var player
var enemy
func _ready():
center_canvas = get_viewport().get_rect().size / 2
tex_ofs = -tex.get_size()/2
player = Entity.new(0, 0)
enemy = Entity.new(0, 200)
set_fixed_process(true)
var player_rotation = 0
var enemy_orbit_angle = PI
var oscillation = 0
var turn_speed = -.01
var move_speed = .02
func _fixed_process(delta):
enemy.pos = player.pos + Vector2(sin(enemy_orbit_angle), cos(enemy_orbit_angle)) * (150 + 60 * sin(oscillation))
player.set_direction(player_rotation)
player_rotation += turn_speed
enemy_orbit_angle += move_speed
oscillation += move_speed
update()
func _draw():
var player_pos = center_canvas + player.pos
draw_texture(tex, player_pos + tex_ofs)
var left = player.facing_direction.angle() - player.fov_edge/2 + PI/2
var right = player.facing_direction.angle() + player.fov_edge/2 + PI/2
var fov_poly = [player_pos]
fov_poly.append(player_pos + Vector2(-cos(left), sin(left)) * 2000)
fov_poly.append(player_pos + Vector2(-cos(right), sin(right)) * 2000)
draw_colored_polygon(fov_poly, Color(0,.8,.6, .8))
if(player.can_see(enemy)):
draw_texture(tex, center_canvas + enemy.pos + tex_ofs, Color(1,0,0,.8))
else:
draw_texture(tex, center_canvas + enemy.pos + tex_ofs)