Capricorn 76
Waypoints example.
Waypoints can be used for navigation, camera tracking, or as place holders in your world.

ScriptENGINE provides waypoint navigating functions using the
A* algorithm to determine an optimal path between
two waypoints based upon the 'cost' of each waypoint connection.

The navigator algorithm will try to find a path that minimses the cost.
The waypoints connection costs are numbers.

---------------------------
---------------------------
-- Waypoints example
-- Copyright (c) 2007-09 Capricorn 76 Pty. Ltd.
--
-- Waypoints can be used for navigation,
-- camera tracking, or as place holders in your world.
--
-- ScriptENGINE provides waypoint navigating functions using the
-- A* algorithm to determine an optimal path between
-- two waypoints based upon the 'cost' of each waypoint connection.
--
-- The navigator algorithm will try to find a path that minimses the cost.
-- The waypoints connection costs are numbers.
--
-- Assign costs to your waypoint connections according to
-- how difficult you expect the navigation between the two
-- waypoints should be.
--
-- NOTE:
-- The ./Demos/runDemo.e76script is a utility script used to run all the examples.
-- It loads the world, calls the OnWorldLoad(), OnWorldUnload() functions, and runs the graphics engine loop, waiting for the user to press escape.
---------------------------
---------------------------

local startingWaypointId    = nil;
local goalWaypointId        = nil;
local subMouseButton        = nil;
local GUI_PATH_ID_START = 2000; -- The waypoint path is highlighted using GUI labels. The label identifiers start from this constant

---------------------------
-- PRIVATE FUNCTION
-- Add the Waypoint Path Step to the GUI
---------------------------
function _addWaypointPathStep(idx, waypointId)
    ------------------------
    -- Create a new text box showing the waypoint path index.
    -- Place it where the waypoint is.
    ------------------------
    local pos = IGraphics:getScreenPosFrom3DPos(IWorld:getEntityPosition(waypointId), IWorld:getActiveCameraId());
    local rect = IMisc:rectCreate(IMisc:pointGetX(pos) - 0.02,
                                    IMisc:pointGetY(pos) - 0.02,
                                    IMisc:pointGetX(pos) + 0.025,
                                    IMisc:pointGetY(pos) + 0.025);
    local id = GUI_PATH_ID_START + idx;
    if (IGraphics:createText(id, tostring(idx), rect, true, false, true, '1 0 0 1')) then
        IGraphics:setStaticTextAlignment(id, GUI_JUSTIFY_CENTER, GUI_JUSTIFY_CENTER);
    end
end

------------------------
-- PRIVATE FUNCTION
-- Clean the old waypoint path data
------------------------
local function _clearOldPath()
    local idx = 1;
    while (IGraphics:controlExists(GUI_PATH_ID_START + idx)) do
        IGraphics:removeControl(GUI_PATH_ID_START + idx);
        idx = idx + 1;
    end
end

---------------------------
-- PRIVATE FUNCTION
-- Use the A* waypoint processing
-- to calculate the optimal path between
-- the starting and goal waypoints
---------------------------
local function _doAStarProcessing()
    if (startingWaypointId) then
        ------------------------
        -- Remove the previous text boxes we added to the screen
        ------------------------
        _clearOldPath();
        _addWaypointPathStep(1, startingWaypointId);

        ------------------------
        -- If we have a start and end waypoint, then calculate a path
        ------------------------
        if (goalWaypointId) then
            print('----------------\n' ..
                  'Calculating A* path between ' ..
                  IWorld:getEntityName(startingWaypointId) ..
                  ' and ' ..
                  IWorld:getEntityName(goalWaypointId) .. '...');

            _clearOldPath();

            ------------------------
            -- Create a navigator.
            -- This id can be stored in your entity's databag and reused
            -- instead of recreating it every time.
            ------------------------
            nav = IWorld:createWaypointNavigator();

            -- We can add multiple starting waypoints,
            -- and the navigator will determine the best one to use
            IWorld:addWaypointNavigatorStart(nav, startingWaypointId);

            -- Must have one goal waypoint only
            IWorld:setWaypointNavigatorGoal(nav, goalWaypointId);

            ------------------------
            -- Perform the search
            ------------------------
            if (IWorld:doWaypointNavigatorSearch(nav)) then
                print(  'PATH FOUND.',
                        'Total path cost:',     IWorld:getWaypointNavigatorSolutionCost(nav),
                        'Total path steps:',    IWorld:getWaypointNavigatorSolutionCount(nav));


                -- Iterate over the solution waypoints
                IWorld:moveWaypointNavigatorSolutionStart(nav);
                local idx = 1;
                while (IWorld:isWaypointNavigatorSolutionValid(nav)) do
                    currentWaypointId = IWorld:getWaypointNavigatorSolutionCurrentId(nav);

                    print('Waypoint', idx, ':', IWorld:getEntityName(currentWaypointId));
                    _addWaypointPathStep(idx, currentWaypointId);

                    idx = idx + 1;
                    IWorld:moveWaypointNavigatorSolutionNext(nav);
                end
            else
                print(  'NO VALID PATH FOUND');
            end

            IWorld:destroyWaypointNavigator(nav);
        end

    end
end

---------------------------
-- runDemo.e76script calls this function when the world is loaded
---------------------------
function OnWorldLoad(worldName)
    --print('OnWorldLoad');

    ---------------------------
    -- Load constants that are used in this example
    ---------------------------
    IApp:loadScript(IApp:getExeFilePath() .. './Constants/constantsGuiAlignments.e76script');

    ---------------------------
    -- Load example's GUI
    ---------------------------
    OnWorldLoadGui();

    ---------------------------
    -- We want to show the waypoints and connections in run-mode
    -- so we set their edit-mod property on.
    ---------------------------
    IWorld:forEachWaypoint('OnWaypointRunMode');

    ---------------------------
    -- Subscribe to mouse button events
    ---------------------------
    subMouseButton = IEvents:subscribeMouseButton('OnMouseButton');

end

function OnWaypointRunMode(waypointId)
    -- Change the editmode property of the waypoint
    -- and readd it to the scene
    IWorld:setEntityEditMode(waypointId, true);
    IWorld:addWaypoint(waypointId);

end

---------------------------
-- Load GUI specific to this example
---------------------------
function OnWorldLoadGui()
    IGraphics:loadGui('help');
end

---------------------------
-- runDemo.e76script calls this function when the world is unloaded
---------------------------
function OnWorldUnload(worldName)
    --print('OnWorldUnload');

    ---------------------------
    -- Unsubscribe to events.
    -- We will no longer will notified of the events.
    ---------------------------
    IEvents:unsubscribe(subMouseButton);

end

---------------------------
-- Mouse button callback handler
---------------------------
function OnMouseButton(eventType, mouseX, mouseY, mouseWheel, userData)

    ---------------------------
    -- Left button clicked. Select starting waypoint.
    ---------------------------
    if      (IMouse:leftButtonClicked(eventType)) then

        -- Only allow waypoints to be selected by using a filter.
        startingWaypointId = IGraphics:rayCastMousePositionFilter(IWorld:getWaypointTypeId());

        if (startingWaypointId) then
            print('Starting waypoint selected:', IWorld:getEntityName(startingWaypointId));

            _doAStarProcessing();
        end

        return true;    -- We handled the event;

    ---------------------------
    -- Right button clicked. Select ending waypoint.
    -- Calculate waypoint path and display it.
    ---------------------------
    elseif  (IMouse:rightButtonClicked(eventType)) then

        -- Only allow waypoints to be selected by using a filter.
        goalWaypointId = IGraphics:rayCastMousePositionFilter(IWorld:getWaypointTypeId());

        if (goalWaypointId) then
            print('Goal waypoint selected:', IWorld:getEntityName(goalWaypointId));

            _doAStarProcessing();
        end

        return true;    -- We handled the event;
    end
end

Copyright © 2006-23 Sep 2009 Capricorn 76 Pty. Ltd. (created on Wed Sep 23 16:49:12 2009)