pythonentityursina

why on_click action doesn't work on ursina?


I tried to make this code execute code when the green cubes are clicked, and it worked:

from ursina import *

nav = [None, 0, 0]
"""nav[0] store the cubes hovered data so i can get information and change it (type(nav[0]) is entity)
nav[1] tells the player if it must move or not (0 and 1) 
nav[2] tells the program when it should reset nav (when the movement is over)(with 0 and 1)"""

# Configurable variables
movement_speed = 0.1  # Speed of movement when holding WASD
rotation_speed = 40   # Speed of rotation (degrees per frame)
camera_distance = 15  # Distance of the camera from the player
target_threshold = 0.5  # Distance threshold to stop moving when reaching the target
control_mode = 0  # 0 = Move with click, 1 = Move with WASD
"""this is the function that is called when a green cube is clicked and store the cubes data (like position) 
and tells the player when to start movement (with nav[1]):"""
def click():
    global nav
    nav = [
        (mouse.hovered_entity.x,#cube x
     mouse.hovered_entity.y + mouse.hovered_entity.scale[1] / 2,#cube y
      mouse.hovered_entity.z),#cube z
       1, #start or not 

        0]#reset nav to None when movment is over
"""I made a function that gets the player position (a_x,a_y) and
 block that is clicked(b_x ,b_y) and return the I and j vector that i** 2+j**2 =1 . 
 if the distance of player from block is less than 0.5 it will make nav [ 2] True so the player will stop: """
def normalize_direction_vector(x_a, y_a, x_b, y_b):
    global nav
    dx = x_b - x_a
    dy = y_b - y_a
    magnitude = math.sqrt(dx**2 + dy**2)
    try:
        i = dx / magnitude
        if magnitude < target_threshold:
            nav[2] = 1
    except:
        i = 0
        nav[2] = 1
    try:
        j = dy / magnitude
        if magnitude < target_threshold:
            nav[2] = 1
    except:
        j = 0
        nav[2] = 1
    return [i, j]
#u is the app(windows) and a is the player a white cube:
u = Ursina(fullscreen=1, development_mode=1)

a = Entity(model="cube", position=Vec3(0, 0, 0), scale=Vec3(1, 1, 1))
""" I made a 10*10 platform of green cubes 
which when they are clicked the player will move and their action is click()
and the problem is when they are clicked the function doesnt run:"""
for i in range(10):
    for j in range(10):
        block = Entity(model='cube',
         color=color.green,
          position=Vec3(i, -1, j),
           scale=Vec3(1, 1, 1),
            collider="box",
             on_click=click)#here is the problem

cam = camera

while True:
    if held_keys["escape"]:
        break
    """makes the cube that was hovered by the mouse green again so
    it won't remain black(because the mouse makes the hovered blocks black later in the code.
    I don't want them to remain black and become green again if mouse is not hovered) """
    try:
        hov.color = color.green
    except:#if hov is None 
        pass

    if control_mode:  # WASD Movement
        if held_keys["w"]:
            a.x += movement_speed
            a.z += movement_speed
        if held_keys["s"]:
            a.x -= movement_speed
            a.z -= movement_speed
        if held_keys["a"]:
            a.x -= movement_speed
            a.z += movement_speed
        if held_keys["d"]:
            a.x += movement_speed
            a.z -= movement_speed
    """makes the player move if hov[1] is true(the player must move)
     and make the player move (using line 16-35) .
     if hov[2] is true the nav list will reset and the player won't move anymore:"""
    if not control_mode and nav[1]:  # Click-to-move
        c = normalize_direction_vector(a.x, a.z, nav[0][0], nav[0][2])
        if nav[2]:#check if the plauer must stop
            nav = [None, 0, 0]
        else:
            a.x += c[0] * movement_speed
            a.z += c[1] * movement_speed
    """controls the camera and the players view with mouse"""
    a.look_at((mouse.x * 5 + a.x, 0, mouse.y * 5 + a.z,))
    cam.x = a.x - camera_distance
    cam.y = a.y + camera_distance
    cam.z = a.z - camera_distance
    cam.look_at((mouse.x * 5 + a.x, 0, mouse.y * 5 + a.z,))
    a.rotate(Vec3(0, rotation_speed, 0))
    """folowing line will make the hovered cubes with mouse black ."""
    try:
        hov = mouse.hovered_entity
        mouse.hovered_entity.color = color.black
    except:#if no hov cubes
        pass

    
    u.step()#update


But when I changed my code to be in a function, like this:

python
from ursina import *

def click():
        global nav
        nav = [(mouse.hovered_entity.x, mouse.hovered_entity.y + mouse.hovered_entity.scale[1] / 2, mouse.hovered_entity.z), 1, 0]


def runner():

    nav = [None, 0, 0]

    # Configurable variables
    movement_speed = 0.1  # Speed of movement when holding WASD
    rotation_speed = 40   # Speed of rotation (degrees per frame)
    camera_distance = 15  # Distance of the camera from the player
    target_threshold = 0.5  # Distance threshold to stop moving when reaching the target
    control_mode = 0  # 0 = Move with click, 1 = Move with WASD

   

    def normalize_direction_vector(x_a, y_a, x_b, y_b):
        global nav
        dx = x_b - x_a
        dy = y_b - y_a
        magnitude = math.sqrt(dx**2 + dy**2)
        try:
            i = dx / magnitude
            if magnitude < target_threshold:
                nav[2] = 1
        except:
            i = 0
            nav[2] = 1
        try:
            j = dy / magnitude
            if magnitude < target_threshold:
                nav[2] = 1
        except:
            j = 0
            nav[2] = 1
        return [i, j]

    u = Ursina(fullscreen=1, development_mode=1)

    a = Entity(model="cube", position=Vec3(0, 0, 0), scale=Vec3(1, 1, 1))
    for i in range(10):
        for j in range(10):
            block = Entity(model='cube', color=color.green, position=Vec3(i, -1, j), scale=Vec3(1, 1, 1), collider="box", on_click=click)

    cam = camera

    while True:
        if held_keys["escape"]:
            break

        try:
            hov.color = color.green
        except:
            pass

        if control_mode:  # WASD Movement
            if held_keys["w"]:
                a.x += movement_speed
                a.z += movement_speed
            if held_keys["s"]:
                a.x -= movement_speed
                a.z -= movement_speed
            if held_keys["a"]:
                a.x -= movement_speed
                a.z += movement_speed
            if held_keys["d"]:
                a.x += movement_speed
                a.z -= movement_speed

        if not control_mode and nav[1]:  # Click-to-move
            c = normalize_direction_vector(a.x, a.z, nav[0][0], nav[0][2])
            if nav[2]:
                nav = [None, 0, 0]
            else:
                a.x += c[0] * movement_speed
                a.z += c[1] * movement_speed

        a.look_at((mouse.x * 5 + a.x, 0, mouse.y * 5 + a.z,))
        cam.x = a.x - camera_distance
        cam.y = a.y + camera_distance
        cam.z = a.z - camera_distance
        cam.look_at((mouse.x * 5 + a.x, 0, mouse.y * 5 + a.z,))

        try:
            hov = mouse.hovered_entity
            mouse.hovered_entity.color = color.black
        except:
            pass

        a.rotate(Vec3(0, rotation_speed, 0))
        u.step()
runner()

it does not work anymore.

Also, I do not want to change my code to do something like have the mainloop contained in a function, I want to keep it contained in a while loop.


Solution

  • I believe it is because of the global keywords inside of the click() function. defining nav inside the runner() function gets scoped to that function, so the global nav inside click() doesn't get referred to the same variable

    use nav as a global variable outside the runner function.

    nav = [None, 0, 0]

    and inside the functions include

    global nav

    Here is the discord if you need any more help

    https://discord.gg/Rh7Xfd98BU