├── .gitignore ├── CODE_OF_CONDUCT.md ├── Config ├── DefaultEditor.ini ├── DefaultEditorPerProjectUserSettings.ini ├── DefaultEngine.ini ├── DefaultGame.ini └── DefaultInput.ini ├── Content ├── Blueprints │ ├── AI │ │ ├── BP_Player.uasset │ │ ├── Example_A │ │ │ ├── BB_Example_A.uasset │ │ │ ├── BP_AIController_Example_A.uasset │ │ │ ├── BP_Player_Example_A.uasset │ │ │ └── BT_Example_A.uasset │ │ └── Example_B │ │ │ ├── BB_Example_B.uasset │ │ │ ├── BP_AIController_Example_B.uasset │ │ │ ├── BTS_BindBump.uasset │ │ │ ├── BTT_Explode.uasset │ │ │ ├── BTT_Respawn.uasset │ │ │ └── BT_Example_B.uasset │ ├── Extras │ │ ├── BP_Portal.uasset │ │ └── MI_Portal.uasset │ ├── Framework │ │ ├── BP_GameMode.uasset │ │ ├── BP_PlayerController.uasset │ │ └── BP_SpectatorPawn.uasset │ ├── HexagonalGrid │ │ └── BP_HexGrid.uasset │ └── Interfaces │ │ └── BPI_GraphAStarExample.uasset ├── FX │ └── NS_Explosion.uasset ├── Mannequin │ ├── Animations │ │ ├── ThirdPersonIdle.uasset │ │ ├── ThirdPersonJump_End.uasset │ │ ├── ThirdPersonJump_Loop.uasset │ │ ├── ThirdPersonJump_Start.uasset │ │ ├── ThirdPersonRun.uasset │ │ ├── ThirdPersonWalk.uasset │ │ ├── ThirdPerson_AnimBP.uasset │ │ ├── ThirdPerson_IdleRun_2D.uasset │ │ └── ThirdPerson_Jump.uasset │ └── Character │ │ ├── Materials │ │ ├── M_UE4Man_Body.uasset │ │ ├── M_UE4Man_ChestLogo.uasset │ │ └── MaterialLayers │ │ │ ├── ML_GlossyBlack_Latex_UE4.uasset │ │ │ ├── ML_Plastic_Shiny_Beige.uasset │ │ │ ├── ML_Plastic_Shiny_Beige_LOGO.uasset │ │ │ ├── ML_SoftMetal_UE4.uasset │ │ │ ├── T_ML_Aluminum01.uasset │ │ │ ├── T_ML_Aluminum01_N.uasset │ │ │ ├── T_ML_Rubber_Blue_01_D.uasset │ │ │ └── T_ML_Rubber_Blue_01_N.uasset │ │ ├── Mesh │ │ ├── SK_Mannequin.uasset │ │ ├── SK_Mannequin_PhysicsAsset.uasset │ │ └── UE4_Mannequin_Skeleton.uasset │ │ └── Textures │ │ ├── UE4Man_Logo_N.uasset │ │ ├── UE4_LOGO_CARD.uasset │ │ ├── UE4_Mannequin_MAT_MASKA.uasset │ │ └── UE4_Mannequin__normals.uasset ├── Maps │ ├── Example_A.umap │ ├── Example_B.umap │ └── StartLevel.umap ├── Materials │ ├── MI_Cursor_Decal.uasset │ ├── MI_SelectionDecal.uasset │ └── M_Cursor_Decal.uasset ├── Meshes │ ├── MI_Hex100.uasset │ ├── MI_Hex100_Blocking.uasset │ ├── M_Hex100.uasset │ ├── SM_Hex100x1.uasset │ └── T_Hex100.uasset ├── Splash │ ├── EdSplash.png │ ├── EdSplash.uasset │ ├── Splash.png │ └── Splash.uasset └── UI │ ├── WB_CameraHotkeys.uasset │ ├── WB_Controls.uasset │ ├── WB_DebugHotkeys.uasset │ └── WB_MainHUD.uasset ├── GHImages ├── AIControllerB.PNG ├── pathsegment.PNG ├── placeholder.txt └── supportedagents.PNG ├── GraphAStarExample.uproject ├── README.md ├── Resources ├── HexBanner600x200.png ├── HexIcon256x256.ico └── HexIcon256x256.png └── Source ├── GraphAStarExample.Target.cs ├── GraphAStarExample ├── GraphAStarExample.Build.cs ├── GraphAStarExample.cpp ├── GraphAStarExample.h ├── GraphAStarExampleGameModeBase.cpp ├── GraphAStarExampleGameModeBase.h ├── Private │ ├── AI │ │ ├── GraphAStarNavMesh.cpp │ │ ├── HGAIController.cpp │ │ └── HGPathFollowingComponent.cpp │ └── HexGrid │ │ └── HexGrid.cpp └── Public │ ├── AI │ ├── GraphAStarNavMesh.h │ ├── HGAIController.h │ └── HGPathFollowingComponent.h │ └── HexGrid │ ├── HGTypes.h │ └── HexGrid.h └── GraphAStarExampleEditor.Target.cs /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio 2015 user specific files 2 | .vs/ 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | *.ipa 33 | 34 | # These project files can be generated by the engine 35 | *.xcodeproj 36 | *.xcworkspace 37 | *.sln 38 | *.suo 39 | *.opensdf 40 | *.sdf 41 | *.VC.db 42 | *.VC.opendb 43 | 44 | # Precompiled Assets 45 | SourceArt/**/*.png 46 | SourceArt/**/*.tga 47 | 48 | # Binary Files 49 | Binaries/* 50 | Plugins/*/Binaries/* 51 | 52 | # Builds 53 | Build/* 54 | 55 | # Whitelist PakBlacklist-.txt files 56 | !Build/*/ 57 | Build/*/** 58 | !Build/*/PakBlacklist*.txt 59 | 60 | # Don't ignore icon files in Build 61 | !Build/**/*.ico 62 | 63 | # Built data for maps 64 | *_BuiltData.uasset 65 | 66 | # Configuration files generated by the Editor 67 | Saved/* 68 | 69 | # Compiled source files for the engine to use 70 | Intermediate/* 71 | Plugins/*/Intermediate/* 72 | 73 | # Cache files for the editor to use 74 | DerivedDataCache/* 75 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at yuribelleri@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /Config/DefaultEditor.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Config/DefaultEditorPerProjectUserSettings.ini: -------------------------------------------------------------------------------- 1 | 2 | [/Script/EditorStyle.EditorStyleSettings] 3 | bEnableUserEditorLayoutManagement=True 4 | ColorVisionDeficiencyPreviewType=NormalVision 5 | ColorVisionDeficiencySeverity=3 6 | bColorVisionDeficiencyCorrection=False 7 | bColorVisionDeficiencyCorrectionPreviewWithDeficiency=False 8 | SelectionColor=(R=0.728000,G=0.364000,B=0.003000,A=1.000000) 9 | PressedSelectionColor=(R=0.701000,G=0.225000,B=0.003000,A=1.000000) 10 | InactiveSelectionColor=(R=0.250000,G=0.250000,B=0.250000,A=1.000000) 11 | KeyboardFocusColor=(R=0.000000,G=0.000000,B=0.000000,A=0.000000) 12 | EditorWindowBackgroundColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000) 13 | EditorMainWindowBackgroundOverride=(ImageSize=(X=32.000000,Y=32.000000),Margin=(Left=0.000000,Top=0.000000,Right=0.000000,Bottom=0.000000),TintColor=(SpecifiedColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),ColorUseRule=UseColor_Specified),ResourceObject=None,ResourceName="",UVRegion=(Min=(X=0.000000,Y=0.000000),Max=(X=0.000000,Y=0.000000),bIsValid=0),DrawAs=Image,Tiling=NoTile,Mirroring=NoMirror,ImageType=NoImage,bIsDynamicallyLoaded=False) 14 | EditorChildWindowBackgroundOverride=(ImageSize=(X=32.000000,Y=32.000000),Margin=(Left=0.000000,Top=0.000000,Right=0.000000,Bottom=0.000000),TintColor=(SpecifiedColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),ColorUseRule=UseColor_Specified),ResourceObject=None,ResourceName="",UVRegion=(Min=(X=0.000000,Y=0.000000),Max=(X=0.000000,Y=0.000000),bIsValid=0),DrawAs=Image,Tiling=NoTile,Mirroring=NoMirror,ImageType=NoImage,bIsDynamicallyLoaded=False) 15 | bResetEditorWindowBackgroundSettings=False 16 | bUseSmallToolBarIcons=False 17 | bUseGrid=True 18 | RegularColor=(R=0.035000,G=0.035000,B=0.035000,A=1.000000) 19 | RuleColor=(R=0.008000,G=0.008000,B=0.008000,A=1.000000) 20 | CenterColor=(R=0.000000,G=0.000000,B=0.000000,A=1.000000) 21 | GridSnapSize=16 22 | bEnableWindowAnimations=False 23 | bShowFriendlyNames=True 24 | bExpandConfigurationMenus=False 25 | bShowProjectMenus=True 26 | bShowLaunchMenus=True 27 | LogBackgroundColor=(R=0.015996,G=0.015996,B=0.015996,A=1.000000) 28 | LogSelectionBackgroundColor=(R=0.008023,G=0.008023,B=0.008023,A=1.000000) 29 | LogNormalColor=(R=0.720000,G=0.720000,B=0.720000,A=1.000000) 30 | LogCommandColor=(R=0.033105,G=0.723055,B=0.033105,A=1.000000) 31 | LogWarningColor=(R=0.921875,G=0.691406,B=0.000000,A=1.000000) 32 | LogErrorColor=(R=1.000000,G=0.052083,B=0.060957,A=1.000000) 33 | bShowAllAdvancedDetails=False 34 | bShowHiddenPropertiesWhilePlaying=False 35 | LogFontSize=9 36 | LogTimestampMode=None 37 | bPromoteOutputLogWarningsDuringPIE=False 38 | AssetEditorOpenLocation=MainWindow 39 | bEnableColorizedEditorTabs=True 40 | 41 | 42 | -------------------------------------------------------------------------------- /Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [/Script/Engine.Engine] 2 | +ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/GraphAStarExample") 3 | +ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/GraphAStarExample") 4 | +ActiveClassRedirects=(OldClassName="TP_BlankGameModeBase",NewClassName="GraphAStarExampleGameModeBase") 5 | 6 | [/Script/HardwareTargeting.HardwareTargetingSettings] 7 | TargetedHardwareClass=Desktop 8 | AppliedTargetedHardwareClass=Desktop 9 | DefaultGraphicsPerformance=Maximum 10 | AppliedDefaultGraphicsPerformance=Maximum 11 | 12 | [/Script/EngineSettings.GameMapsSettings] 13 | EditorStartupMap=/Game/Maps/StartLevel.StartLevel 14 | GameDefaultMap=/Game/Maps/StartLevel.StartLevel 15 | 16 | [/Script/NavigationSystem.NavigationSystemV1] 17 | DefaultAgentName=Default 18 | CrowdManagerClass=/Script/AIModule.CrowdManager 19 | bAutoCreateNavigationData=True 20 | bSpawnNavDataInNavBoundsLevel=False 21 | bAllowClientSideNavigation=False 22 | bShouldDiscardSubLevelNavData=True 23 | bTickWhilePaused=False 24 | bInitialBuildingLocked=False 25 | bSkipAgentHeightCheckWhenPickingNavData=False 26 | DataGatheringMode=Instant 27 | bGenerateNavigationOnlyAroundNavigationInvokers=False 28 | ActiveTilesUpdateInterval=1.000000 29 | +SupportedAgents=(Name="Default",Color=(B=0,G=255,R=140,A=63),DefaultQueryExtent=(X=50.000000,Y=50.000000,Z=250.000000),NavDataClass=/Script/GraphAStarExample.GraphAStarNavMesh,AgentRadius=35.000000,AgentHeight=144.000000,AgentStepHeight=-1.000000,NavWalkingSearchHeightScale=0.500000,PreferredNavData=/Script/GraphAStarExample.GraphAStarNavMesh,bCanCrouch=False,bCanJump=False,bCanWalk=False,bCanSwim=False,bCanFly=False) 30 | SupportedAgentsMask=(bSupportsAgent0=True,bSupportsAgent1=True,bSupportsAgent2=True,bSupportsAgent3=True,bSupportsAgent4=True,bSupportsAgent5=True,bSupportsAgent6=True,bSupportsAgent7=True,bSupportsAgent8=True,bSupportsAgent9=True,bSupportsAgent10=True,bSupportsAgent11=True,bSupportsAgent12=True,bSupportsAgent13=True,bSupportsAgent14=True,bSupportsAgent15=True) 31 | DirtyAreasUpdateFreq=60.000000 32 | -------------------------------------------------------------------------------- /Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | 2 | [/Script/EngineSettings.GeneralProjectSettings] 3 | ProjectID=8261F454442D080560E4A190D1633E69 4 | -------------------------------------------------------------------------------- /Config/DefaultInput.ini: -------------------------------------------------------------------------------- 1 | 2 | [/Script/Engine.InputSettings] 3 | -AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 4 | -AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 5 | -AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 6 | -AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) 7 | -AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) 8 | -AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) 9 | +AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 10 | +AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 11 | +AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 12 | +AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 13 | +AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) 14 | +AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) 15 | +AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 16 | +AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 17 | +AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 18 | +AxisConfig=(AxisKeyName="MotionController_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 19 | +AxisConfig=(AxisKeyName="MotionController_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 20 | +AxisConfig=(AxisKeyName="MotionController_Left_TriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 21 | +AxisConfig=(AxisKeyName="MotionController_Left_Grip1Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 22 | +AxisConfig=(AxisKeyName="MotionController_Left_Grip2Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 23 | +AxisConfig=(AxisKeyName="MotionController_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 24 | +AxisConfig=(AxisKeyName="MotionController_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 25 | +AxisConfig=(AxisKeyName="MotionController_Right_TriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 26 | +AxisConfig=(AxisKeyName="MotionController_Right_Grip1Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 27 | +AxisConfig=(AxisKeyName="MotionController_Right_Grip2Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 28 | +AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 29 | +AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 30 | +AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 31 | +AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 32 | +AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 33 | +AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 34 | +AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 35 | +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 36 | +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 37 | +AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 38 | +AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 39 | +AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 40 | +AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 41 | +AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 42 | +AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 43 | +AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 44 | +AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 45 | +AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 46 | +AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 47 | +AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 48 | +AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 49 | +AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 50 | +AxisConfig=(AxisKeyName="OculusGo_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 51 | +AxisConfig=(AxisKeyName="OculusGo_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 52 | +AxisConfig=(AxisKeyName="OculusGo_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 53 | +AxisConfig=(AxisKeyName="OculusGo_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 54 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 55 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 56 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 57 | +AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 58 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 59 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 60 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 61 | +AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 62 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 63 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 64 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 65 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 66 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 67 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 68 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 69 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 70 | +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Touch",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 71 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 72 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 73 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 74 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 75 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 76 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 77 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 78 | +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) 79 | bAltEnterTogglesFullscreen=True 80 | bF11TogglesFullscreen=True 81 | bUseMouseForTouch=False 82 | bEnableMouseSmoothing=True 83 | bEnableFOVScaling=True 84 | bCaptureMouseOnLaunch=True 85 | bAlwaysShowTouchInterface=False 86 | bShowConsoleOnFourFingerTap=True 87 | bEnableGestureRecognizer=False 88 | bUseAutocorrect=False 89 | DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown 90 | DefaultViewportMouseLockMode=LockOnCapture 91 | FOVScale=0.011110 92 | DoubleClickTime=0.200000 93 | +ActionMappings=(ActionName="ZoomIn",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MouseScrollDown) 94 | +ActionMappings=(ActionName="ZoomOut",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MouseScrollUp) 95 | +ActionMappings=(ActionName="Quit",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Escape) 96 | +ActionMappings=(ActionName="Focus",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=F) 97 | +AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=W) 98 | +AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=D) 99 | +AxisMappings=(AxisName="MoveForward",Scale=-1.000000,Key=S) 100 | +AxisMappings=(AxisName="MoveRight",Scale=-1.000000,Key=A) 101 | DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks 102 | -ConsoleKeys=Tilde 103 | +ConsoleKeys=Tilde 104 | +ConsoleKeys=Backslash 105 | 106 | 107 | -------------------------------------------------------------------------------- /Content/Blueprints/AI/BP_Player.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/AI/BP_Player.uasset -------------------------------------------------------------------------------- /Content/Blueprints/AI/Example_A/BB_Example_A.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/AI/Example_A/BB_Example_A.uasset -------------------------------------------------------------------------------- /Content/Blueprints/AI/Example_A/BP_AIController_Example_A.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/AI/Example_A/BP_AIController_Example_A.uasset -------------------------------------------------------------------------------- /Content/Blueprints/AI/Example_A/BP_Player_Example_A.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/AI/Example_A/BP_Player_Example_A.uasset -------------------------------------------------------------------------------- /Content/Blueprints/AI/Example_A/BT_Example_A.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/AI/Example_A/BT_Example_A.uasset -------------------------------------------------------------------------------- /Content/Blueprints/AI/Example_B/BB_Example_B.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/AI/Example_B/BB_Example_B.uasset -------------------------------------------------------------------------------- /Content/Blueprints/AI/Example_B/BP_AIController_Example_B.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/AI/Example_B/BP_AIController_Example_B.uasset -------------------------------------------------------------------------------- /Content/Blueprints/AI/Example_B/BTS_BindBump.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/AI/Example_B/BTS_BindBump.uasset -------------------------------------------------------------------------------- /Content/Blueprints/AI/Example_B/BTT_Explode.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/AI/Example_B/BTT_Explode.uasset -------------------------------------------------------------------------------- /Content/Blueprints/AI/Example_B/BTT_Respawn.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/AI/Example_B/BTT_Respawn.uasset -------------------------------------------------------------------------------- /Content/Blueprints/AI/Example_B/BT_Example_B.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/AI/Example_B/BT_Example_B.uasset -------------------------------------------------------------------------------- /Content/Blueprints/Extras/BP_Portal.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/Extras/BP_Portal.uasset -------------------------------------------------------------------------------- /Content/Blueprints/Extras/MI_Portal.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/Extras/MI_Portal.uasset -------------------------------------------------------------------------------- /Content/Blueprints/Framework/BP_GameMode.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/Framework/BP_GameMode.uasset -------------------------------------------------------------------------------- /Content/Blueprints/Framework/BP_PlayerController.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/Framework/BP_PlayerController.uasset -------------------------------------------------------------------------------- /Content/Blueprints/Framework/BP_SpectatorPawn.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/Framework/BP_SpectatorPawn.uasset -------------------------------------------------------------------------------- /Content/Blueprints/HexagonalGrid/BP_HexGrid.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/HexagonalGrid/BP_HexGrid.uasset -------------------------------------------------------------------------------- /Content/Blueprints/Interfaces/BPI_GraphAStarExample.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Blueprints/Interfaces/BPI_GraphAStarExample.uasset -------------------------------------------------------------------------------- /Content/FX/NS_Explosion.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/FX/NS_Explosion.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Animations/ThirdPersonIdle.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Animations/ThirdPersonIdle.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Animations/ThirdPersonJump_End.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Animations/ThirdPersonJump_End.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Animations/ThirdPersonJump_Loop.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Animations/ThirdPersonJump_Loop.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Animations/ThirdPersonJump_Start.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Animations/ThirdPersonJump_Start.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Animations/ThirdPersonRun.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Animations/ThirdPersonRun.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Animations/ThirdPersonWalk.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Animations/ThirdPersonWalk.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Animations/ThirdPerson_AnimBP.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Animations/ThirdPerson_AnimBP.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Animations/ThirdPerson_IdleRun_2D.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Animations/ThirdPerson_IdleRun_2D.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Animations/ThirdPerson_Jump.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Animations/ThirdPerson_Jump.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Materials/M_UE4Man_Body.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Materials/M_UE4Man_Body.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Materials/M_UE4Man_ChestLogo.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Materials/M_UE4Man_ChestLogo.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Materials/MaterialLayers/ML_GlossyBlack_Latex_UE4.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Materials/MaterialLayers/ML_Plastic_Shiny_Beige_LOGO.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Materials/MaterialLayers/ML_SoftMetal_UE4.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_D.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Materials/MaterialLayers/T_ML_Rubber_Blue_01_N.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Mesh/SK_Mannequin.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Mesh/SK_Mannequin.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Mesh/SK_Mannequin_PhysicsAsset.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Mesh/SK_Mannequin_PhysicsAsset.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Mesh/UE4_Mannequin_Skeleton.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Mesh/UE4_Mannequin_Skeleton.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Textures/UE4Man_Logo_N.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Textures/UE4Man_Logo_N.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Textures/UE4_LOGO_CARD.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Textures/UE4_LOGO_CARD.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Textures/UE4_Mannequin_MAT_MASKA.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Textures/UE4_Mannequin_MAT_MASKA.uasset -------------------------------------------------------------------------------- /Content/Mannequin/Character/Textures/UE4_Mannequin__normals.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Mannequin/Character/Textures/UE4_Mannequin__normals.uasset -------------------------------------------------------------------------------- /Content/Maps/Example_A.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Maps/Example_A.umap -------------------------------------------------------------------------------- /Content/Maps/Example_B.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Maps/Example_B.umap -------------------------------------------------------------------------------- /Content/Maps/StartLevel.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Maps/StartLevel.umap -------------------------------------------------------------------------------- /Content/Materials/MI_Cursor_Decal.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Materials/MI_Cursor_Decal.uasset -------------------------------------------------------------------------------- /Content/Materials/MI_SelectionDecal.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Materials/MI_SelectionDecal.uasset -------------------------------------------------------------------------------- /Content/Materials/M_Cursor_Decal.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Materials/M_Cursor_Decal.uasset -------------------------------------------------------------------------------- /Content/Meshes/MI_Hex100.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Meshes/MI_Hex100.uasset -------------------------------------------------------------------------------- /Content/Meshes/MI_Hex100_Blocking.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Meshes/MI_Hex100_Blocking.uasset -------------------------------------------------------------------------------- /Content/Meshes/M_Hex100.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Meshes/M_Hex100.uasset -------------------------------------------------------------------------------- /Content/Meshes/SM_Hex100x1.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Meshes/SM_Hex100x1.uasset -------------------------------------------------------------------------------- /Content/Meshes/T_Hex100.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Meshes/T_Hex100.uasset -------------------------------------------------------------------------------- /Content/Splash/EdSplash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Splash/EdSplash.png -------------------------------------------------------------------------------- /Content/Splash/EdSplash.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Splash/EdSplash.uasset -------------------------------------------------------------------------------- /Content/Splash/Splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Splash/Splash.png -------------------------------------------------------------------------------- /Content/Splash/Splash.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/Splash/Splash.uasset -------------------------------------------------------------------------------- /Content/UI/WB_CameraHotkeys.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/UI/WB_CameraHotkeys.uasset -------------------------------------------------------------------------------- /Content/UI/WB_Controls.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/UI/WB_Controls.uasset -------------------------------------------------------------------------------- /Content/UI/WB_DebugHotkeys.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/UI/WB_DebugHotkeys.uasset -------------------------------------------------------------------------------- /Content/UI/WB_MainHUD.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Content/UI/WB_MainHUD.uasset -------------------------------------------------------------------------------- /GHImages/AIControllerB.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/GHImages/AIControllerB.PNG -------------------------------------------------------------------------------- /GHImages/pathsegment.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/GHImages/pathsegment.PNG -------------------------------------------------------------------------------- /GHImages/placeholder.txt: -------------------------------------------------------------------------------- 1 | This is a placeholder 2 | -------------------------------------------------------------------------------- /GHImages/supportedagents.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/GHImages/supportedagents.PNG -------------------------------------------------------------------------------- /GraphAStarExample.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "4.27", 4 | "Category": "", 5 | "Description": "", 6 | "Modules": [ 7 | { 8 | "Name": "GraphAStarExample", 9 | "Type": "Runtime", 10 | "LoadingPhase": "Default", 11 | "AdditionalDependencies": [ 12 | "NavigationSystem", 13 | "Engine", 14 | "AIModule" 15 | ] 16 | } 17 | ], 18 | "Plugins": [ 19 | { 20 | "Name": "PluginBrowser", 21 | "Enabled": true 22 | }, 23 | { 24 | "Name": "Text3D", 25 | "Enabled": true 26 | }, 27 | { 28 | "Name": "Niagara", 29 | "Enabled": true 30 | }, 31 | { 32 | "Name": "NiagaraExtras", 33 | "Enabled": true 34 | }, 35 | { 36 | "Name": "OculusVR", 37 | "Enabled": false, 38 | "SupportedTargetPlatforms": [ 39 | "Win32", 40 | "Win64", 41 | "Android" 42 | ] 43 | }, 44 | { 45 | "Name": "SteamVR", 46 | "Enabled": false, 47 | "SupportedTargetPlatforms": [ 48 | "Win32", 49 | "Win64", 50 | "Linux", 51 | "Mac" 52 | ] 53 | }, 54 | { 55 | "Name": "MegascansPlugin", 56 | "Enabled": false, 57 | "SupportedTargetPlatforms": [ 58 | "Win64", 59 | "Mac", 60 | "Linux" 61 | ] 62 | } 63 | ], 64 | "TargetPlatforms": [ 65 | "WindowsNoEditor" 66 | ] 67 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## This is the "stable" release branch, look at the [master branch](https://github.com/ZioYuri78/GraphAStarExample/tree/Master) for advanced features like: 2 | * Hex Grid Plugin (basic version) 3 | * Simple jump implementation 4 | * EQS support 5 | * Simple A* debug draws 6 | * Simple avoidance 7 | 8 | 9 | # Hexagonal grid pathfinding in Unreal Engine 4 10 | 11 | ## Prerequisites 12 | * [A good knowledge of C++.](https://www.learncpp.com/) 13 | * Red Blob Games articles about hexagonal grid. 14 | * [Hexagonal grid reference.](https://www.redblobgames.com/grids/hexagons/) 15 | * [Hexagonal grid implementation guide.](https://www.redblobgames.com/grids/hexagons/implementation.html) 16 | 17 | ## Readings will let you better understand my example project 18 | * [Epic wiki article about replacing the pathfinder.](https://unrealcommunity.wiki/replacing-the-pathfinder-h6eyeor0) 19 | * [Neatly replacing NavMesh with A* in UE4 by Chris Russel.](https://crussel.net/2016/06/05/neatly-replacing-navmesh-with-a-in-ue4/) 20 | * [Epic wiki article about custom Path Following Component.](https://unrealcommunity.wiki/ai-navigation-in-cpp-customize-path-following-every-tick-2n7ju142) 21 | 22 | ## The GraphAStarExample project 23 | 24 | ![](https://media.giphy.com/media/RJ1aMT0TB7OQXcbVIY/giphy.gif) 25 | 26 | Welcome all to my example project about how to use the Unreal Engine 4 generic graph A* implementation with hexagonal grids, the intent of this project is to guide you on what you need to setup the basics for navigation on hexagonal grids, this is not a complete tutorial but more like a guideline and doesn't cover topics like avoidance, grid transfer etc. 27 | 28 | In the project you will find two examples, A and B, both examples work with the same Pathfinder, Example B is just a bonus that will show you what you can do with a custom PathFollowingComponent. 29 | 30 | ### Table of contents 31 | - [Classes and structs you need to know.](https://github.com/ZioYuri78/GraphAStarExample#classes-and-structs-you-need-to-know) 32 | - [Classes and structs we used in the project.](https://github.com/ZioYuri78/GraphAStarExample#classes-and-structs-we-used-in-the-project) 33 | - [Core Blueprints of the project.](https://github.com/ZioYuri78/GraphAStarExample#core-blueprints-of-the-project) 34 | 35 | ### Classes and Structs you need to know 36 | - [ANavigationData](https://github.com/ZioYuri78/GraphAStarExample#anavigationdata) 37 | - [ARecastNavMesh](https://github.com/ZioYuri78/GraphAStarExample#arecastnavmesh) 38 | - [FGraphAStar](https://github.com/ZioYuri78/GraphAStarExample#fgraphastar) 39 | - [AAIController (optional)](https://github.com/ZioYuri78/GraphAStarExample#aaicontroller-optional) 40 | - [UPathFollowingComponent (optional)](https://github.com/ZioYuri78/GraphAStarExample#upathfollowingcomponent-optional) 41 | 42 | #### ANavigationData 43 | Represents abstract Navigation Data (sub-classed as NavMesh, NavGraph, etc). 44 | Used as a common interface for all navigation types handled by NavigationSystem. 45 | 46 | Here you will find a lot of interesting stuff but the most important for us is the FindPathImplementation class member, this is a function pointer. 47 | ``` 48 | typedef FPathFindingResult (*FFindPathPtr)(const FNavAgentProperties& AgentProperties, const FPathFindingQuery& Query); 49 | FFindPathPtr FindPathImplementation; 50 | ``` 51 | Also the ANavigationData::FindPath function is very important for us, this function return the result of the function pointed by FindPathImplementation. 52 | 53 | ``` 54 | /** 55 | * Synchronously looks for a path from @StartLocation to @EndLocation for agent with properties @AgentProperties. 56 | * NavMesh actor appropriate for specified FNavAgentProperties will be found automatically 57 | * @param ResultPath results are put here 58 | * @return true if path has been found, false otherwise 59 | * 60 | * @note don't make this function virtual! Look at implementation details and its comments for more info. 61 | */ 62 | FORCEINLINE FPathFindingResult FindPath(const FNavAgentProperties& AgentProperties, const FPathFindingQuery& Query) const 63 | { 64 | check(FindPathImplementation); 65 | // this awkward implementation avoids virtual call overhead - it's possible this function will be called a lot 66 | return (*FindPathImplementation)(AgentProperties, Query); 67 | } 68 | ``` 69 | Take a look at the note "don't make this function virtual!", we will come back on it in a while. 70 | 71 | #### ARecastNavMesh 72 | This class inherit from ANavigationData and extend his functionality, everytime you place a NavMeshBoundsVolume in the map an object of this class is created (you can see it in the World Outliner), is the RecastNavMesh-Default object! 73 | 74 | This is the class we have to inherit from! 75 | 76 | Let's look how this class implement the ANavigationData::FindPath function, we already know that this function is not virtual, so how we can implement it? 77 | We have the FindPathImplementation function pointer! 78 | 79 | In the header you can see the ARecastNavMesh::FindPath declaration. 80 | 81 | `static FPathFindingResult FindPath(const FNavAgentProperties& AgentProperties, const FPathFindingQuery& Query);` 82 | 83 | the function is static for a reason, (wiki copy-paste->) *comments in the code explain it’s for performance reasons: Epic are concerned that if a lot of agents call the pathfinder in the same frame the virtual call overhead will accumulate and take too long, so instead the function is declared static and stored in the FindPathImplementation function pointer. 84 | Which means you need to manually set the function pointer in your new navigation class constructor* (or in some other function like i did in my example). 85 | 86 | This is the function where we will implement (and the FindPathImplementation will point) in our inherited class! 87 | 88 | #### FGraphAStar 89 | Finally we are in the (second) core class (ok ok, is a struct) of our example, the FGraphAstar is the Unreal Engine 4 generic implementation of the A* algorithm. 90 | 91 | If you open the (\Engine\Source\Runtime\AIModule\Public\) GraphAStar.h file you will find in the comments an explanation about how to use it, let's look: 92 | 93 | > Generic graph A* implementation. 94 | > TGraph holds graph representation. Needs to implement functions: 95 | 96 | ``` 97 | /* Returns number of neighbors that the graph node identified with NodeRef has */ 98 | int32 GetNeighbourCount(FNodeRef NodeRef) const; 99 | 100 | /* Returns whether given node identification is correct */ 101 | bool IsValidRef(FNodeRef NodeRef) const; 102 | 103 | /* Returns neighbor ref */ 104 | FNodeRef GetNeighbour(const FNodeRef NodeRef, const int32 NeighbourIndex) const; 105 | ``` 106 | > it also needs to specify node type 107 | 108 | `FNodeRef - type used as identification of nodes in the graph` 109 | 110 | > TQueryFilter (FindPath's parameter) filter class is what decides which graph edges can be used and at what cost. It needs to implement following functions: 111 | 112 | ``` 113 | /** 114 | * Used as GetHeuristicCost's multiplier 115 | */ 116 | float GetHeuristicScale() const; 117 | 118 | /** 119 | * Estimate of cost from StartNodeRef to EndNodeRef 120 | */ 121 | float GetHeuristicCost(const int32 StartNodeRef, const int32 EndNodeRef) const; 122 | 123 | /** 124 | * Real cost of traveling from StartNodeRef directly to EndNodeRef 125 | */ 126 | float GetTraversalCost(const int32 StartNodeRef, const int32 EndNodeRef) const; 127 | 128 | /** 129 | * Whether traversing given edge is allowed 130 | */ 131 | bool IsTraversalAllowed(const int32 NodeA, const int32 NodeB) const; 132 | 133 | /** 134 | * Whether to accept solutions that do not reach the goal 135 | */ 136 | bool WantsPartialSolution() const; 137 | ``` 138 | 139 | So we don't have to create a class (ok ok, is a struct) from FGraphAStar but we have to implement the above code in the class which will call the FGraphAStar::FindPath function, in our case this class will be AGraphAStarNavMesh (inherited from ARecastNavMesh). 140 | 141 | You can find a good example on how to do it in (\Engine\Source\Runtime\AIModule\Classes\Navigation\) NavLocalGridData.h 142 | 143 | #### AAIController (optional) 144 | In the project you will find two examples, A and B: 145 | - example A use the default AIController (BP_AIController_Example_A) 146 | - example B use a custom AIController (BP_AIController_Example_B) with a custom PathFollowingComponent (HGPathFollowingComponent). 147 | 148 | To use a custom PathFollowingComponent we have to inherit the AAIController class and tell her which class of the PathFollowingComponent we want to use. 149 | 150 | We will talk about it later, in the AHGAIController section. 151 | 152 | #### UPathFollowingComponent (optional) 153 | This component will let your AI to follow the path, it's full of interesting functions and variables, we will override only two of these functions just to show you they are here and what you can do with a custom PathFollowingComponent. 154 | 155 | We will talk about it later, in the UHGPathFollowingComponent section. 156 | 157 | ### Classes and structs we used in the project 158 | - [AGraphAStarNavMesh (inherited from ARecastNavMesh)](https://github.com/ZioYuri78/GraphAStarExample#agraphastarnavmesh) 159 | - [AHexGrid (inherited from AActor)](https://github.com/ZioYuri78/GraphAStarExample#ahexgrid) 160 | - [HGTypes (a collection of structs used by AHexGrid)](https://github.com/ZioYuri78/GraphAStarExample#hgtypes) 161 | - [AHGAIController (inherited from AAIController)](https://github.com/ZioYuri78/GraphAStarExample#ahgaicontroller-optional) 162 | - [UHGPathFollowingComponent (inherited from UPathFollowingComponent)](https://github.com/ZioYuri78/GraphAStarExample#uhgpathfollowingcomponent-optional) 163 | 164 | #### AGraphAStarNavMesh 165 | Our most important class, where the magic happen! 166 | 167 | Here is where we "integrate" the FGraphAStar implementation and it will be very easy! 168 | Before do that we need a pointer to our AHexGrid class, this will be the hexagonal grid on which we will perform the pathfinding (look at the AHexGrid section). 169 | 170 | ``` 171 | /* Just a pointer to an hexagonal grid actor */ 172 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "GraphAStarExample|NavMesh") 173 | class AHexGrid *HexGrid; 174 | ``` 175 | 176 | We also need a function to set this pointer, this function is one of the key points of our example, it will switch from our FindPath implementation to the ARecastNavMesh::FindPath implementation (the default behavior). 177 | 178 | This can be done at realtime! 179 | 180 | Ok ok, if we do it while the AI is following a path it will finish it before the switch happen. 181 | (On top of my head i think is possible to do the switch on the fly but we will not do it in this project.) 182 | 183 | ``` 184 | /* Set a pointer to an hexagonal grid, it can be nullptr */ 185 | UFUNCTION(BlueprintCallable, Category = "GraphAStarExample|NavMesh") 186 | void SetHexGrid(class AHexGrid *HGrid); 187 | 188 | void AGraphAStarNavMesh::SetHexGrid(AHexGrid *HGrid) 189 | { 190 | if (HGrid) 191 | { 192 | // If the pointer is valid we will use our implementation of the FindPath function 193 | HexGrid = HGrid; 194 | FindPathImplementation = FindPath; 195 | } 196 | else 197 | { 198 | // If the pointer is not valid we will fallback to the default RecastNavMesh implementation 199 | // of the FindPath function (the standard navigation behavior) 200 | // You can also use FindPathImplementation = ARecastNavMesh::FindPath; 201 | // but i start from the assumption that we are inheriting from ARecastNavMesh 202 | HexGrid = nullptr; 203 | FindPathImplementation = Super::FindPath; 204 | 205 | } 206 | } 207 | ``` 208 | 209 | Ok, move on and let me define the FGridPathFilter struct in the header 210 | 211 | ``` 212 | /** 213 | * TQueryFilter (FindPath's parameter) filter class is what decides which graph edges can be used and at what cost. 214 | */ 215 | struct FGridPathFilter 216 | { 217 | FGridPathFilter(const AGraphAStarNavMesh &InNavMeshRef) : NavMeshRef(InNavMeshRef) {} 218 | 219 | /** 220 | * Used as GetHeuristicCost's multiplier 221 | */ 222 | float GetHeuristicScale() const; 223 | 224 | /** 225 | * Estimate of cost from StartNodeRef to EndNodeRef 226 | */ 227 | float GetHeuristicCost(const int32 StartNodeRef, const int32 EndNodeRef) const; 228 | 229 | /** 230 | * Real cost of traveling from StartNodeRef directly to EndNodeRef 231 | */ 232 | float GetTraversalCost(const int32 StartNodeRef, const int32 EndNodeRef) const; 233 | 234 | /** 235 | * Whether traversing given edge is allowed 236 | */ 237 | bool IsTraversalAllowed(const int32 NodeA, const int32 NodeB) const; 238 | 239 | /** 240 | * Whether to accept solutions that do not reach the goal 241 | */ 242 | bool WantsPartialSolution() const; 243 | 244 | protected: 245 | 246 | /** 247 | * A reference to our NavMesh 248 | */ 249 | const AGraphAStarNavMesh &NavMeshRef; 250 | }; 251 | ``` 252 | 253 | as you see this struct also has a reference to our AGraphAStarNavMesh, we will use it in the implementation 254 | 255 | ``` 256 | float FGridPathFilter::GetHeuristicScale() const 257 | { 258 | // For the sake of simplicity we just return 1.f 259 | return 1.0f; 260 | } 261 | 262 | float FGridPathFilter::GetHeuristicCost(const int32 StartNodeRef, const int32 EndNodeRef) const 263 | { 264 | return GetTraversalCost(StartNodeRef, EndNodeRef); 265 | } 266 | 267 | float FGridPathFilter::GetTraversalCost(const int32 StartNodeRef, const int32 EndNodeRef) const 268 | { 269 | // If EndNodeRef is a valid index of the GridTiles array we return the tile cost, 270 | // if not we return 1 because the traversal cost need to be > 0 or the FGraphAStar will stop the execution 271 | // look at GraphAStar.h line 244: ensure(NewTraversalCost > 0); 272 | if (NavMeshRef.HexGrid->GridTiles.IsValidIndex(EndNodeRef)) 273 | { 274 | return NavMeshRef.HexGrid->GridTiles[EndNodeRef].Cost; 275 | } 276 | else 277 | { 278 | return 1.f; 279 | } 280 | } 281 | 282 | bool FGridPathFilter::IsTraversalAllowed(const int32 NodeA, const int32 NodeB) const 283 | { 284 | // If NodeB is a valid index of the GridTiles array we return bIsBlocking, 285 | // if not we assume we can traverse so we return true. 286 | // Here you can make a more complex operation like use a line trace to see 287 | // there is some obstacles (like an enemy), in our example we just use a simple implementation 288 | if (NavMeshRef.HexGrid->GridTiles.IsValidIndex(NodeB)) 289 | { 290 | return !NavMeshRef.HexGrid->GridTiles[NodeB].bIsBlocking; 291 | } 292 | else 293 | { 294 | return true; 295 | } 296 | } 297 | 298 | bool FGridPathFilter::WantsPartialSolution() const 299 | { 300 | // Just return true 301 | return true; 302 | } 303 | ``` 304 | 305 | now what we have to do is declare the FNodeRef typedef and the functions requested by FGraphAStar 306 | 307 | ``` 308 | /* Type used as identification of nodes in the graph */ 309 | typedef int32 FNodeRef; 310 | 311 | /* Returns number of neighbors that the graph node identified with NodeRef has */ 312 | int32 GetNeighbourCount(FNodeRef NodeRef) const; 313 | 314 | /* Returns whether given node identification is correct */ 315 | bool IsValidRef(FNodeRef NodeRef) const; 316 | 317 | /* Returns neighbor ref */ 318 | FNodeRef GetNeighbour(const FNodeRef NodeRef, const int32 NeiIndex) const; 319 | ``` 320 | 321 | ``` 322 | int32 AGraphAStarNavMesh::GetNeighbourCount(FNodeRef NodeRef) const 323 | { 324 | return 6; 325 | } 326 | 327 | bool AGraphAStarNavMesh::IsValidRef(FNodeRef NodeRef) const 328 | { 329 | return HexGrid->CubeCoordinates.IsValidIndex(NodeRef); 330 | } 331 | 332 | AGraphAStarNavMesh::FNodeRef 333 | AGraphAStarNavMesh::GetNeighbour(const FNodeRef NodeRef, const int32 NeiIndex) const 334 | { 335 | FHCubeCoord Neigh{ HexGrid->GetNeighbor(HexGrid->CubeCoordinates[NodeRef], HexGrid->GetDirection(NeiIndex)) }; 336 | return HexGrid->CubeCoordinates.IndexOfByKey(Neigh); 337 | } 338 | ``` 339 | 340 | you will notice we don't do any check on the validity of the HexGrid pointer because (in my opinion) there is no reason to do it, if the HexGrid pointer is null we don't run this code, we run the default implementation of FindPath (the ARecastNavMesh version). 341 | 342 | and finally the real Queen of the entire project, the FindPath function 343 | 344 | ``` 345 | static FPathFindingResult FindPath(const FNavAgentProperties &AgentProperties, const FPathFindingQuery &Query); 346 | ``` 347 | 348 | This function take two parameters: 349 | - FNavAgentProperties &AgentProperties 350 | - FPathFindingQuery &Query 351 | 352 | we will use FPathFindingQuery to get the starting and ending point of our path (let me say on a high level this data is filled by the MoveTo call, is not 100% correct but just to tell you where they come from). 353 | 354 | and return a struct: 355 | - FPathFindingResult 356 | 357 | this struct is important because contains the Path the AI will follow. 358 | 359 | ``` 360 | struct FPathFindingResult 361 | { 362 | FNavPathSharedPtr Path; 363 | ENavigationQueryResult::Type Result; 364 | 365 | FPathFindingResult(ENavigationQueryResult::Type InResult = ENavigationQueryResult::Invalid) : Result(InResult) 366 | { } 367 | 368 | FORCEINLINE bool IsSuccessful() const 369 | { 370 | return Result == ENavigationQueryResult::Success; 371 | } 372 | FORCEINLINE bool IsPartial() const; 373 | }; 374 | ``` 375 | 376 | I will skip the first block of code here because is the same code you can find in the ARecastNavMesh::FindPath, let me say that it "check things" :P 377 | 378 | One of these things is get a "this" pointer, because we are in a static function we don't have the "this" pointer and we can't access to non-static member variables like HexGrid, luckily the FPathFindingQuery contains a pointer to the ANavigationData object. 379 | 380 | `const ANavigationData *Self = Query.NavData.Get();` 381 | 382 | We can cast it our class, this will allow us to access the member variables (and functions), remember, our AGraphAStarNavMesh inherit from ARecastNavMesh that inherit from ANavigationData. 383 | 384 | `const AGraphAStarNavMesh *GraphAStarNavMesh{ Cast(Self) };` 385 | 386 | jump to the core of FindPath, where we finally use the FGraphAStar to compute our A*. 387 | 388 | ``` 389 | // The pathfinder need a starting and ending point, so we create two temporary 390 | // cube coordinates from the Query start and ending location 391 | FHCubeCoord StartCCoord{ GraphAStarNavMesh->HexGrid->WorldToHex(Query.StartLocation) }; 392 | FHCubeCoord EndCCoord{ GraphAStarNavMesh->HexGrid->WorldToHex(Query.EndLocation) }; 393 | 394 | // and than we search in the HexGrid CubeCoordinates array for the index of items 395 | // equals to our temp coordinates. 396 | const int32 StartIdx{ GraphAStarNavMesh->HexGrid->CubeCoordinates.IndexOfByKey(StartCCoord) }; 397 | const int32 EndIdx{ GraphAStarNavMesh->HexGrid->CubeCoordinates.IndexOfByKey(EndCCoord)}; 398 | 399 | // We need the index because the FGraphAStar work with indexes! 400 | 401 | // Here we will store the path generated from the pathfinder 402 | TArray PathIndices; 403 | 404 | // Initialization of the pathfinder, as you can see we pass our GraphAStarNavMesh as parameter, 405 | // so internally it can use the functions we implemented. 406 | FGraphAStar Pathfinder(*GraphAStarNavMesh); 407 | 408 | // and run the A* algorithm, the FGraphAStar::FindPath function want a starting index, an ending index, 409 | // the FGridPathFilter which want our GraphAStarNavMesh as parameter and a reference to the array where 410 | // all the indices of our path will be stored 411 | EGraphAStarResult AStarResult{ Pathfinder.FindPath(StartIdx, EndIdx, FGridPathFilter(*GraphAStarNavMesh), PathIndices) }; 412 | ``` 413 | 414 | as you can see is super easy to use, after we created an array where store the indices of the path and the starting/ending points we create an instance of FGraphAstar with our AGraphAStarNavMesh as a template, we dereference the GraphAStarNavMesh pointer and we pass it as argument. 415 | 416 | `FGraphAStar Pathfinder(*GraphAStarNavMesh);` 417 | 418 | The FGraphAStar::FindPath function want: 419 | - a starting point and an ending point (in form of indices, so not the real coordinates but the indices of the array where they are stored) 420 | - an instance of FGridPathFilter in which we pass the dereferenced GraphAStarNavMesh pointer 421 | - a reference to an array where store the indices of our path points. 422 | 423 | It also return a EGraphAStarResult enum that will tell us if the search failed or succeeded. 424 | 425 | `EGraphAStarResult AStarResult{ Pathfinder.FindPath(StartIdx, EndIdx, FGridPathFilter(*GraphAStarNavMesh), PathIndices) };` 426 | 427 | If the search succeeded we use all the indices stored in the PathIndices array to build up the real Path, we already mentioned the FPathFindingResult struct contains the Path the AI will follow. 428 | 429 | ``` 430 | case SearchSuccess: 431 | 432 | // Search succeeded 433 | Result.Result = ENavigationQueryResult::Success; 434 | 435 | // PathIndices array computed by FGraphAStar will not contain the starting point, so 436 | // we need to add it manually the the Path::PathPoints array 437 | Result.Path->GetPathPoints().Add(FNavPathPoint(Query.StartLocation)); 438 | 439 | // Let's traverse the PathIndices array and build the FNavPathPoints we 440 | // need to add to the Path. 441 | for (const int32 &PathIndex : PathIndices) 442 | { 443 | // Get a temporary Cube Coordinate from our HexGrid 444 | FHCubeCoord CubeCoord{ GraphAStarNavMesh->HexGrid->CubeCoordinates[PathIndex] }; 445 | 446 | // Create a temporary FNavPathPoint 447 | FNavPathPoint PathPoint{}; 448 | 449 | // Because we can create HexGrid with only Cube Coordinates and no tiles 450 | // we look if the current index we are using is a valid index for the GridTiles array 451 | if (GraphAStarNavMesh->HexGrid->GridTiles.IsValidIndex(PathIndex)) 452 | { 453 | // If the index is valid (so we have a HexGrid with tiles) we compute the Location 454 | // of the PathPoint, we use the World Space coordinates of the current Cube Coordinate 455 | // as a base location and we add an offset to the Z axis based on the corresponding 456 | // How to compute the Z axis of the path is up to you, this is only an example! 457 | PathPoint.Location = GraphAStarNavMesh->HexGrid->HexToWorld(CubeCoord) + 458 | FVector(0.f, 0.f, GraphAStarNavMesh->HexGrid->GridTiles[PathIndex].Cost); 459 | } 460 | else 461 | { 462 | // If the current PathIndex isn't a valid index for the GridTiles array 463 | // (so we assume our HexGrid is only a "logical" grid with only cube coordinates and no tiles) 464 | // we simply transform the coordinates from cube space to world space and pass it to the PathPoint 465 | PathPoint.Location = GraphAStarNavMesh->HexGrid->HexToWorld(CubeCoord); 466 | } 467 | 468 | // We finally add the computed PathPoint to the Path::PathPoints array 469 | Result.Path->GetPathPoints().Add(FNavPathPoint(PathPoint)); 470 | } 471 | 472 | // We finished to create the Path so mark it as Ready. 473 | Result.Path->MarkReady(); 474 | break; 475 | ``` 476 | 477 | That's all, the key concept is that FGraphAStar::FindPath work with indices, you don't need to pass the cube coordinates but the indices of them, all the "service" functions we implemented before and the FGridPathFilter struct will take care to use those indices with the cube coordinates array. 478 | 479 | It's more easy to follow the flow of the code than explain it. 480 | 481 | Now we only need to add our AGraphAStarNavMesh to the Navigation System list of supported agents, to do it you simply have to open your Project Settings, search Navigation System under the Engine category, under Agents you will find an empty array called Supported Agents, just click the small + symbol and select GraphAStarNavMesh in Nav Data Class and Preferred Nav Data, rebuild your paths with the Build button in the main toolbar and you are ready to go. 482 | 483 | ![alt supported agents](GHImages/supportedagents.PNG) 484 | 485 | Now you only have to use a MoveTo call (or any MoveTo version) and the AI will follow the A* path computed on the hexagonal grid you placed in the level and passed via SetHexGrid, if you pass a null actor with SetHexGrid the system will fallback to the default pathfindind. 486 | 487 | Look at the BP_PlayerController to see how i pass an existing HexGrid to the NavMesh and how i set the destination to the AI Blackboard, if you open the example Behavior Trees (BT_Example_A/B) you will notice they contains only a MoveTo task to a target location for AI movement! 488 | 489 | 490 | #### AHexGrid 491 | The HexGrid class is a rough implementation of an hexagonal grid actor, the most important function is AHexGrid::CreateGrid, is based on the [Red Blob Games implementation](https://www.redblobgames.com/grids/hexagons/implementation.html#map-shapes) so i strongly suggest to read the linked article, here we just want to point out two things. 492 | 493 | 1. I'm using a delegate at each step of the "creation" so we can delegate the creation of the tiles to another function or Blueprint Event, this delegate is optional so you are not obliged to use it, to do it (make it optional) you need to mark the function parameter with the AutoCreateRefTerm UFUNCTION metadata 494 | 495 | ``` 496 | UFUNCTION(BlueprintCallable, Category = "GraphAStarExample|HexGrid", meta = (AutoCreateRefTerm = "CreationStepDelegate")) 497 | void CreateGrid(const FHTileLayout &TLayout, const int32 GridRadius, const FCreationStepDelegate &CreationStepDelegate); 498 | ``` 499 | 500 | if this delegate is bounded will be executed, if not...just not. 501 | 502 | `CreationStepDelegate.ExecuteIfBound(TileLayout, CCoord);` 503 | 504 | 2. We are using a simple formula to compute how much space we want to reserve in our arrays, w do this because the "Add" call can be expensive when used a lot of time and if we create a grid with radius > 10 tiles it will be called hundreds of times in the for loop. 505 | 506 | ``` 507 | int32 Size{ 1 }; 508 | for (int32 i{ 1 }; i <= Radius; ++i) 509 | { 510 | Size += 6 * i; 511 | } 512 | CubeCoordinates.Reserve(Size); 513 | 514 | // Check if we provided a delegate, if yes we also reserve space in the GridTiles array. 515 | if (CreationStepDelegate.IsBound()) 516 | { 517 | GridTiles.Reserve(Size); 518 | } 519 | ``` 520 | 521 | You can find more info about optimizing TArray usage for performance [HERE](https://www.unrealengine.com/en-US/blog/optimizing-tarray-usage-for-performance). 522 | 523 | I also defined a rough representation of Tiles, the FHexTile struct contains the basic data you need for a simple grid with tiles. 524 | 525 | All the other functions of this class are pretty simple and each of them as a link to the relative Red Blob Game article section 526 | s. 527 | 528 | #### HGTypes 529 | This header just contains data structures and enums used to create the hexagonal grid, the Red Blob Games articles explain better than me how they work and why. 530 | 531 | Heach structs and enums in the code has a link to the relative Red Blob Games article. 532 | 533 | #### AHGAIController (optional) 534 | We continue discuss about why we have to inherit this class if we want to use a custom PathFollowingComponent. 535 | 536 | We need to tell the inherited AAIController class which class of the PathFollowingComponent we want to use, to do it we need the ObjectInitializer parent class member and his SetDefaultSubobjectClass function. 537 | We will call it on the initialization of the base class in the derived class constructor... very easy right? 538 | 539 | Ok ok, here i'm failing hard with my english (i'm very sorry) but don't worry, it's easier to do it than to say it: 540 | 541 | ``` 542 | AHGAIController::AHGAIController(const FObjectInitializer &ObjectInitializer /*= FObjectInitializer::Get()*/) 543 | : Super(ObjectInitializer.SetDefaultSubobjectClass(TEXT("PathFollowingComponent"))) 544 | { 545 | } 546 | ``` 547 | 548 | That's all, in the contructor we use `: Super()` and we pass the ObjectInitializer (and the SetDefaultSubobjectClass function call) to the constructor of the parent class, this will replace the default PathFollowingComponent class. 549 | 550 | So now when we create an AIController Blueprint based to our class instead of have the default PathFollowingComponent it will have our version, but keep attention to the TEXT() parameter, to work well it must be the same as the default component of the parent AIController class! 551 | 552 | `TEXT("PathFollowingComponent")` 553 | 554 | In your Blueprint you will still see the component named "PathFollowingComponent" but if you go over it with the mouse you will see it is the derived version. (so magic) 555 | 556 | ![alt PathFollowing Component](GHImages/AIControllerB.PNG) 557 | 558 | **NOTE:** 559 | There is a SetPathFollowingComponet function in the AAIController (also is BlueprintCallable) but i still have to figure out how it work, that's why i preferred the ObjectInitializer method. 560 | 561 | #### UHGPathFollowingComponent (optional) 562 | With this class we want to show you how powerfull this component can be, in our example we override two functions and we will do something very simple. 563 | 564 | The first function we are overriding is OnActorBump: 565 | 566 | ``` 567 | /** called when moving agent collides with another actor */ 568 | virtual void OnActorBump(AActor *SelfActor, AActor *OtherActor, FVector NormalImpulse, const FHitResult &Hit) override; 569 | ``` 570 | 571 | Is called when the Pawn possesed by the AIController bump against another actor (it's like a BeginOverlap, or a Hit, you already know this kind of behavior). 572 | 573 | Instead of expose it to Blueprint we decided to create a delegate and bind to it where we want. 574 | 575 | ``` 576 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnActorBumpDelegate, const FVector&, BumpLocation); 577 | . 578 | . 579 | . 580 | /** 581 | * Executed if a "Bump" happen, we bind this delegate on the activation of our Behavior Tree Service BTS_BindBump 582 | */ 583 | UPROPERTY(BlueprintAssignable, Category = "GraphAStarExample|PathFollowingComponent") 584 | FOnActorBumpDelegate OnActorBumped; 585 | ``` 586 | 587 | In our example we will bind it on the activation of a Behavior Tree Service (see how in the BTS_BindBump blueprint). 588 | 589 | In the OnActorBump implementation we look if the bump happen when we are moving or waiting, if yes we Broadcast the location of the other actor involved in the bump. 590 | 591 | ``` 592 | void UHGPathFollowingComponent::OnActorBump(AActor *SelfActor, AActor *OtherActor, FVector NormalImpulse, const FHitResult &Hit) 593 | { 594 | Super::OnActorBump(SelfActor, OtherActor, NormalImpulse, Hit); 595 | 596 | // Let's see if we are moving or waiting. 597 | if (GetStatus() != EPathFollowingStatus::Idle) 598 | { 599 | // Just broadcast the event. 600 | OnActorBumped.Broadcast(OtherActor->GetActorLocation()); 601 | } 602 | } 603 | ``` 604 | 605 | The second function we decided to override is one of the most imnportant we can find in the PathFollowingComponent, the FollowPathSegment function, is the main path follow function and it tick while we are traveling the path! 606 | 607 | We will use it for simple debug purposes, we draw the current followed path, the start point and the end point of the current path segment we are traveling! 608 | 609 | ``` 610 | /** follow current path segment */ 611 | virtual void FollowPathSegment(float DeltaTime) override; 612 | 613 | void UHGPathFollowingComponent::FollowPathSegment(float DeltaTime) 614 | { 615 | Super::FollowPathSegment(DeltaTime); 616 | 617 | /** 618 | * FollowPathSegment is the main UE4 Path Follow tick function, and so when you want to add completely 619 | * custom coding you can use this function as your starting point to adjust normal UE4 path behavior! 620 | * 621 | * Let me show you a simple example with some debug drawings. 622 | */ 623 | 624 | if (Path && DrawDebug) 625 | { 626 | // Just draw the current path 627 | Path->DebugDraw(MyNavData, FColor::White, nullptr, false); 628 | 629 | // Draw the start point of the current path segment we are traveling. 630 | FNavPathPoint CurrentPathPoint{}; 631 | FNavigationPath::GetPathPoint(&Path->AsShared().Get(), GetCurrentPathIndex(), CurrentPathPoint); 632 | DrawDebugLine(GetWorld(), CurrentPathPoint.Location, CurrentPathPoint.Location + FVector(0.f, 0.f, 200.f), FColor::Blue); 633 | DrawDebugSphere(GetWorld(), CurrentPathPoint.Location + FVector(0.f, 0.f, 200.f), 25.f, 16, FColor::Blue); 634 | 635 | // Draw the end point of the current path segment we are traveling. 636 | FNavPathPoint NextPathPoint{}; 637 | FNavigationPath::GetPathPoint(&Path->AsShared().Get(), GetNextPathIndex(), NextPathPoint); 638 | DrawDebugLine(GetWorld(), NextPathPoint.Location, NextPathPoint.Location + FVector(0.f, 0.f, 200.f), FColor::Green); 639 | DrawDebugSphere(GetWorld(), NextPathPoint.Location + FVector(0.f, 0.f, 200.f), 25.f, 16, FColor::Green); 640 | } 641 | } 642 | ``` 643 | 644 | ![alt pathsegment](GHImages/pathsegment.PNG) 645 | 646 | The PathFollowingComponent also has a member variable called MyNavData (really Epic?), this variable is a pointer to the ANavigationData but wait a moment, the ANavigationData is the parent class of ARecastNavMesh class that also is the parent class of GraphAStarNavMesh! 647 | 648 | We can cast this pointer to our class and use the public functions and variables! 649 | In our example we are not using it but we did it just for the sake of knowledge. 650 | 651 | ### Core Blueprints of the project 652 | - AI 653 | - BP_Player 654 | - Example A 655 | - BB_Example_A 656 | - BP_AIController_Example_A 657 | - BT_Example_A 658 | - Example B 659 | - BB_Example_B 660 | - BP_AIController_Example_B 661 | - BTS_BindBump 662 | - BTT_Explode 663 | - BTT_Respawn 664 | - BT_Example_B 665 | - Extras 666 | - BP_Portal 667 | - Framework 668 | - BP_GameMode 669 | - BP_PlayerController 670 | - BP_SpectatorPawn 671 | - HexagonalGrid 672 | - BP_HexGrid 673 | - Interfaces 674 | - BPI_GraphAStarExample 675 | 676 | All the Blueprints are commented so open the project and look at them. 677 | 678 | ## Conclusions 679 | There is a lot of topic i didn't cover here, i can only suggest you to explore the code of the key parent classes and experiment, have fun! 680 | 681 | -------------------------------------------------------------------------------- /Resources/HexBanner600x200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Resources/HexBanner600x200.png -------------------------------------------------------------------------------- /Resources/HexIcon256x256.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Resources/HexIcon256x256.ico -------------------------------------------------------------------------------- /Resources/HexIcon256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZioYuri78/GraphAStarExample/a6efc78b7b516b14425a717ba35c45b6b96264da/Resources/HexIcon256x256.png -------------------------------------------------------------------------------- /Source/GraphAStarExample.Target.cs: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | public class GraphAStarExampleTarget : TargetRules 7 | { 8 | public GraphAStarExampleTarget( TargetInfo Target) : base(Target) 9 | { 10 | Type = TargetType.Game; 11 | 12 | ExtraModuleNames.AddRange( new string[] { "GraphAStarExample" } ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Source/GraphAStarExample/GraphAStarExample.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class GraphAStarExample : ModuleRules 6 | { 7 | public GraphAStarExample(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); 12 | 13 | PrivateDependencyModuleNames.AddRange(new string[] { }); 14 | 15 | // Uncomment if you are using Slate UI 16 | // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); 17 | 18 | // Uncomment if you are using online features 19 | // PrivateDependencyModuleNames.Add("OnlineSubsystem"); 20 | 21 | // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/GraphAStarExample/GraphAStarExample.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "GraphAStarExample.h" 4 | #include "Modules/ModuleManager.h" 5 | 6 | IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, GraphAStarExample, "GraphAStarExample" ); 7 | 8 | -------------------------------------------------------------------------------- /Source/GraphAStarExample/GraphAStarExample.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | -------------------------------------------------------------------------------- /Source/GraphAStarExample/GraphAStarExampleGameModeBase.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | 4 | #include "GraphAStarExampleGameModeBase.h" 5 | 6 | bool AGraphAStarExampleGameModeBase::IsPIE() const 7 | { 8 | return GetWorld() ? GetWorld()->WorldType == EWorldType::PIE : false; 9 | } 10 | -------------------------------------------------------------------------------- /Source/GraphAStarExample/GraphAStarExampleGameModeBase.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/GameModeBase.h" 7 | #include "GraphAStarExampleGameModeBase.generated.h" 8 | 9 | /** 10 | * 11 | */ 12 | UCLASS() 13 | class GRAPHASTAREXAMPLE_API AGraphAStarExampleGameModeBase : public AGameModeBase 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | 19 | UFUNCTION(BlueprintCallable, Category = "GraphAStarExample|Game Mode") 20 | bool IsPIE() const; 21 | }; 22 | -------------------------------------------------------------------------------- /Source/GraphAStarExample/Private/AI/GraphAStarNavMesh.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "GraphAStarNavMesh.h" 5 | #include "HexGrid/HexGrid.h" 6 | #include "AIModule/Public/GraphAStar.h" 7 | 8 | DEFINE_LOG_CATEGORY(LogGraphAStarExample_NavMesh) 9 | 10 | 11 | //==== FGridPathFilter functions implementation === 12 | // In these functions we do not check if the HexGrid is valid because it must be! 13 | // Remember, if the HexGrid is a nullptr we will never use this code 14 | // but we fallback to the RecastNavMesh implementation of it. 15 | 16 | float FGridPathFilter::GetHeuristicScale() const 17 | { 18 | // For the sake of simplicity we just return 1.f 19 | return 1.0f; 20 | } 21 | 22 | float FGridPathFilter::GetHeuristicCost(const int32 StartNodeRef, const int32 EndNodeRef) const 23 | { 24 | return GetTraversalCost(StartNodeRef, EndNodeRef); 25 | } 26 | 27 | float FGridPathFilter::GetTraversalCost(const int32 StartNodeRef, const int32 EndNodeRef) const 28 | { 29 | // If EndNodeRef is a valid index of the GridTiles array we return the tile cost, 30 | // if not we return 1 because the traversal cost need to be > 0 or the FGraphAStar will stop the execution 31 | // look at GraphAStar.h line 244: ensure(NewTraversalCost > 0); 32 | if (NavMeshRef.HexGrid->GridTiles.IsValidIndex(EndNodeRef)) 33 | { 34 | return NavMeshRef.HexGrid->GridTiles[EndNodeRef].Cost; 35 | } 36 | else 37 | { 38 | return 1.f; 39 | } 40 | } 41 | 42 | bool FGridPathFilter::IsTraversalAllowed(const int32 NodeA, const int32 NodeB) const 43 | { 44 | // If NodeB is a valid index of the GridTiles array we return bIsBlocking, 45 | // if not we assume we can traverse so we return true. 46 | // Here you can make a more complex operation like use a line trace to see 47 | // there is some obstacles (like an enemy), in our example we just use a simple implementation 48 | if (NavMeshRef.HexGrid->GridTiles.IsValidIndex(NodeB)) 49 | { 50 | return !NavMeshRef.HexGrid->GridTiles[NodeB].bIsBlocking; 51 | } 52 | else 53 | { 54 | return true; 55 | } 56 | 57 | } 58 | 59 | bool FGridPathFilter::WantsPartialSolution() const 60 | { 61 | // Just return true 62 | return true; 63 | } 64 | //==== END OF FGridPathFilter functions implementation ==== 65 | 66 | 67 | FPathFindingResult AGraphAStarNavMesh::FindPath(const FNavAgentProperties &AgentProperties, const FPathFindingQuery &Query) 68 | { 69 | // ================================================================================================= 70 | // The first part is the same of RecastNavMesh::FindPath implementation, the only difference is 71 | // the cast of ANavigationData to our class. 72 | 73 | SCOPE_CYCLE_COUNTER(STAT_Navigation_HGASPathfinding); 74 | CSV_SCOPED_TIMING_STAT_EXCLUSIVE(Pathfinding); 75 | 76 | // Because we are in a static function we don't have a "this" pointer and we can't access to class member variables like HexGrid 77 | // but luckily the FPathFindingQuery contain a pointer to the ANavigationData object. 78 | const ANavigationData *Self = Query.NavData.Get(); 79 | check(Cast(Self)); 80 | 81 | // Now we can cast to our class, this will allow us to access the member variables (and functions). 82 | // NOTE: remember, our AGraphAStarNavMesh inherit from ARecastNavMesh that inherit from ANavigationData so we can do the cast. 83 | const AGraphAStarNavMesh *GraphAStarNavMesh{ Cast(Self) }; 84 | 85 | if (Self == NULL) 86 | { 87 | return ENavigationQueryResult::Error; 88 | } 89 | 90 | // This struct contains the result of our search and the Path that the AI will follow 91 | FPathFindingResult Result(ENavigationQueryResult::Error); 92 | 93 | FNavigationPath *NavPath = Query.PathInstanceToFill.Get(); 94 | FHexNavMeshPath *NavMeshPath = NavPath ? NavPath->CastPath() : nullptr; 95 | 96 | if (NavMeshPath) 97 | { 98 | Result.Path = Query.PathInstanceToFill; 99 | NavMeshPath->ResetForRepath(); 100 | } 101 | else 102 | { 103 | Result.Path = Self->CreatePathInstance(Query); 104 | NavPath = Result.Path.Get(); 105 | NavMeshPath = NavPath ? NavPath->CastPath() : nullptr; 106 | } 107 | 108 | const FNavigationQueryFilter *NavFilter = Query.QueryFilter.Get(); 109 | if (NavMeshPath && NavFilter) 110 | { 111 | NavMeshPath->ApplyFlags(Query.NavDataFlags); 112 | 113 | const FVector AdjustedEndLocation = NavFilter->GetAdjustedEndLocation(Query.EndLocation); 114 | if ((Query.StartLocation - AdjustedEndLocation).IsNearlyZero() == true) 115 | { 116 | Result.Path->GetPathPoints().Reset(); 117 | Result.Path->GetPathPoints().Add(FNavPathPoint(AdjustedEndLocation)); 118 | Result.Result = ENavigationQueryResult::Success; 119 | } 120 | // ============ END OF SAME CODE AS RECASTNAVMESH ======================================================== 121 | 122 | else 123 | { 124 | // ====================== BEGIN OF OUR CODE =========================================================== 125 | 126 | // Reset the PathPoints array 127 | Result.Path->GetPathPoints().Reset(); 128 | 129 | // The pathfinder need a starting and ending point, so we create two temporary 130 | // cube coordinates from the Query start and ending location 131 | FHCubeCoord StartCCoord{ GraphAStarNavMesh->HexGrid->WorldToHex(Query.StartLocation) }; 132 | FHCubeCoord EndCCoord{ GraphAStarNavMesh->HexGrid->WorldToHex(Query.EndLocation) }; 133 | 134 | // and than we search in the HexGrid CubeCoordinates array for the index of items 135 | // equals to our temp coordinates. 136 | const int32 StartIdx{ GraphAStarNavMesh->HexGrid->GridCoordinates.IndexOfByKey(StartCCoord) }; 137 | const int32 EndIdx{ GraphAStarNavMesh->HexGrid->GridCoordinates.IndexOfByKey(EndCCoord)}; 138 | 139 | // We need the index because the FGraphAStar work with indexes! 140 | 141 | // Here we will store the path generated from the pathfinder 142 | TArray PathIndices; 143 | 144 | // Initialization of the pathfinder, as you can see we pass our GraphAStarNavMesh as parameter, 145 | // so internally it can use the functions we implemented. 146 | FGraphAStar Pathfinder(*GraphAStarNavMesh); 147 | 148 | // and run the A* algorithm, the FGraphAStar::FindPath function want a starting index, an ending index, 149 | // the FGridPathFilter which want our GraphAStarNavMesh as parameter and a reference to the array where 150 | // all the indices of our path will be stored 151 | EGraphAStarResult AStarResult{ Pathfinder.FindPath(StartIdx, EndIdx, FGridPathFilter(*GraphAStarNavMesh), PathIndices) }; 152 | 153 | // The FGraphAStar::FindPath return a EGraphAStarResult enum, we need to assign the right 154 | // value to the FPathFindingResult (that is returned by AGraphAStarNavMesh::FindPath) based on this. 155 | // In the first three cases are simple and process the three "bad" results 156 | switch (AStarResult) 157 | { 158 | case GoalUnreachable: 159 | Result.Result = ENavigationQueryResult::Invalid; 160 | break; 161 | 162 | case InfiniteLoop: 163 | Result.Result = ENavigationQueryResult::Error; 164 | break; 165 | 166 | case SearchFail: 167 | Result.Result = ENavigationQueryResult::Fail; 168 | break; 169 | 170 | // The search was successful so let's process the computed path, remember, right now 171 | // we only have an array of indexes so we have to compute the path locations 172 | // from these indexes and pass them to the PathPoints array of the Path 173 | // that the AI will follow. 174 | case SearchSuccess: 175 | 176 | // Search succeeded 177 | Result.Result = ENavigationQueryResult::Success; 178 | 179 | // PathIndices array computed by FGraphAStar will not contain the starting point, so 180 | // we need to add it manually the the Path::PathPoints array 181 | Result.Path->GetPathPoints().Add(FNavPathPoint(Query.StartLocation)); 182 | 183 | // Let's traverse the PathIndices array and build the FNavPathPoints we 184 | // need to add to the Path. 185 | for (const int32 &PathIndex : PathIndices) 186 | { 187 | // Get a temporary Cube Coordinate from our HexGrid 188 | FHCubeCoord GridCoord{ GraphAStarNavMesh->HexGrid->GridCoordinates[PathIndex] }; 189 | 190 | // Create a temporary FNavPathPoint 191 | FNavPathPoint PathPoint{}; 192 | 193 | // Because we can create HexGrid with only Cube Coordinates and no tiles 194 | // we look if the current index we are using is a valid index for the GridTiles array 195 | if (GraphAStarNavMesh->HexGrid->GridTiles.IsValidIndex(PathIndex)) 196 | { 197 | // If the index is valid (so we have a HexGrid with tiles) we compute the Location 198 | // of the PathPoint, we use the World Space coordinates of the current Cube Coordinate 199 | // as a base location and we add an offset to the Z. 200 | // How to compute the Z axis of the path is up to you, this is only an example! 201 | PathPoint.Location = GraphAStarNavMesh->HexGrid->HexToWorld(GridCoord) + 202 | FVector(0.f, 0.f, GraphAStarNavMesh->HexGrid->GridTiles[PathIndex].Cost + 203 | GraphAStarNavMesh->PathPointZOffset); 204 | } 205 | else 206 | { 207 | // If the current PathIndex isn't a valid index for the GridTiles array 208 | // (so we assume our HexGrid is only a "logical" grid with only cube coordinates and no tiles) 209 | // we simply transform the coordinates from cube space to world space and pass it to the PathPoint 210 | PathPoint.Location = GraphAStarNavMesh->HexGrid->HexToWorld(GridCoord); 211 | } 212 | 213 | // We finally add the computed PathPoint to the Path::PathPoints array 214 | Result.Path->GetPathPoints().Add(FNavPathPoint(PathPoint)); 215 | } 216 | 217 | // We finished to create the Path so mark it as Ready. 218 | Result.Path->MarkReady(); 219 | break; 220 | } 221 | // =========================== END OF OUR CODE ============================================================ 222 | } 223 | } 224 | 225 | return Result; 226 | } 227 | 228 | 229 | void AGraphAStarNavMesh::SetHexGrid(AHexGrid *HGrid) 230 | { 231 | if (HGrid) 232 | { 233 | // If the pointer is valid we will use our implementation of the FindPath function 234 | HexGrid = HGrid; 235 | FindPathImplementation = FindPath; 236 | } 237 | else 238 | { 239 | // If the pointer is not valid we will fallback to the default RecastNavMesh implementation 240 | // of the FindPath function (the standard navigation behavior) 241 | // You can also use FindPathImplementation = ARecastNavMesh::FindPath; 242 | // but i start from the assumption that we are inheriting from ARecastNavMesh 243 | HexGrid = nullptr; 244 | FindPathImplementation = Super::FindPath; 245 | } 246 | } 247 | 248 | 249 | ////////////////////////////////////////////////////////////////////////// 250 | // FGraphAStar: TGraph 251 | // Functions implementation for our FGraphAStar struct 252 | int32 AGraphAStarNavMesh::GetNeighbourCount(FNodeRef NodeRef) const 253 | { 254 | return 6; 255 | } 256 | 257 | bool AGraphAStarNavMesh::IsValidRef(FNodeRef NodeRef) const 258 | { 259 | return HexGrid->GridCoordinates.IsValidIndex(NodeRef); 260 | } 261 | 262 | AGraphAStarNavMesh::FNodeRef 263 | AGraphAStarNavMesh::GetNeighbour(const FNodeRef NodeRef, const int32 NeiIndex) const 264 | { 265 | FHCubeCoord Neigh{ HexGrid->GetNeighbor(HexGrid->GridCoordinates[NodeRef], HexGrid->GetDirection(NeiIndex)) }; 266 | return HexGrid->GridCoordinates.IndexOfByKey(Neigh); 267 | } 268 | ////////////////////////////////////////////////////////////////////////// 269 | -------------------------------------------------------------------------------- /Source/GraphAStarExample/Private/AI/HGAIController.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "HGAIController.h" 5 | #include "HGPathFollowingComponent.h" 6 | 7 | /** 8 | * Here we assign our HGPathFollowingComponent class to the HGAIController so when we make an instance of this class it will use our component instead of the default, 9 | * keep attention on the TEXT() parameter, to work well it must be the same as the default component of the parent AIController class! 10 | */ 11 | AHGAIController::AHGAIController(const FObjectInitializer &ObjectInitializer /*= FObjectInitializer::Get()*/) 12 | : Super(ObjectInitializer.SetDefaultSubobjectClass(TEXT("PathFollowingComponent"))) 13 | { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Source/GraphAStarExample/Private/AI/HGPathFollowingComponent.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "HGPathFollowingComponent.h" 5 | #include "DrawDebugHelpers.h" 6 | 7 | 8 | void UHGPathFollowingComponent::BeginPlay() 9 | { 10 | Super::BeginPlay(); 11 | 12 | // We cast the MyNavData parent class member so we can expose it to Blueprint. 13 | GraphAStarNavMesh = Cast(MyNavData); 14 | } 15 | 16 | 17 | void UHGPathFollowingComponent::OnActorBump(AActor *SelfActor, AActor *OtherActor, FVector NormalImpulse, const FHitResult &Hit) 18 | { 19 | Super::OnActorBump(SelfActor, OtherActor, NormalImpulse, Hit); 20 | 21 | // Let's see if we are moving or waiting. 22 | if ((SelfActor->GetClass() == OtherActor->GetClass()) && (GetStatus() != EPathFollowingStatus::Idle)) 23 | { 24 | // Just broadcast the event. 25 | OnActorBumped.Broadcast(OtherActor->GetActorLocation()); 26 | } 27 | 28 | } 29 | 30 | 31 | void UHGPathFollowingComponent::FollowPathSegment(float DeltaTime) 32 | { 33 | Super::FollowPathSegment(DeltaTime); 34 | 35 | /** 36 | * FollowPathSegment is the main UE4 Path Follow tick function, and so when you want to add completely 37 | * custom coding you can use this function as your starting point to adjust normal UE4 path behavior! 38 | * 39 | * Let me show you a simple example with some debug drawings. 40 | */ 41 | 42 | if (Path && DrawDebug) 43 | { 44 | // Just draw the current path 45 | Path->DebugDraw(MyNavData, FColor::White, nullptr, false); 46 | 47 | // Draw the start point of the current path segment we are traveling. 48 | FNavPathPoint CurrentPathPoint{}; 49 | FNavigationPath::GetPathPoint(&Path->AsShared().Get(), GetCurrentPathIndex(), CurrentPathPoint); 50 | DrawDebugLine(GetWorld(), CurrentPathPoint.Location, CurrentPathPoint.Location + FVector(0.f, 0.f, 200.f), FColor::Blue); 51 | DrawDebugSphere(GetWorld(), CurrentPathPoint.Location + FVector(0.f, 0.f, 200.f), 25.f, 16, FColor::Blue); 52 | 53 | // Draw the end point of the current path segment we are traveling. 54 | FNavPathPoint NextPathPoint{}; 55 | FNavigationPath::GetPathPoint(&Path->AsShared().Get(), GetNextPathIndex(), NextPathPoint); 56 | DrawDebugLine(GetWorld(), NextPathPoint.Location, NextPathPoint.Location + FVector(0.f, 0.f, 200.f), FColor::Green); 57 | DrawDebugSphere(GetWorld(), NextPathPoint.Location + FVector(0.f, 0.f, 200.f), 25.f, 16, FColor::Green); 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /Source/GraphAStarExample/Private/HexGrid/HexGrid.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "HexGrid.h" 5 | 6 | DEFINE_LOG_CATEGORY(LogGraphAStarExample_HexGrid); 7 | 8 | // Sets default values 9 | AHexGrid::AHexGrid() 10 | { 11 | // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. 12 | PrimaryActorTick.bCanEverTick = true; 13 | 14 | } 15 | 16 | // Called when the game starts or when spawned 17 | void AHexGrid::BeginPlay() 18 | { 19 | Super::BeginPlay(); 20 | 21 | } 22 | 23 | // Called every frame 24 | void AHexGrid::Tick(float DeltaTime) 25 | { 26 | Super::Tick(DeltaTime); 27 | 28 | } 29 | 30 | void AHexGrid::CreateGrid(const FHTileLayout &TLayout, const int32 GridRadius, const FCreationStepDelegate &CreationStepDelegate) 31 | { 32 | SCOPE_CYCLE_COUNTER(STAT_CreateGrid); 33 | 34 | 35 | // https://www.unrealengine.com/en-US/blog/optimizing-tarray-usage-for-performance 36 | // preallocate array memory 37 | // R1 = 1 + 6*1 38 | // R2 = 1 + 6*1 + 6*2 39 | // R3 = 1 + 6*1 + 6*2 + 6*3 40 | // R4 = 1 + 6*1 + 6*2 + 6*3 + 6*4 41 | // R5 = ....... 42 | int32 Size{ 1 }; 43 | for (int32 i{ 1 }; i <= Radius; ++i) 44 | { 45 | Size += 6 * i; 46 | } 47 | GridCoordinates.Reserve(Size); 48 | 49 | // Check if we provided a delegate, if yes we also reserve space in the GridTiles array. 50 | if (CreationStepDelegate.IsBound()) 51 | { 52 | GridTiles.Reserve(Size); 53 | } 54 | else 55 | { 56 | UE_LOG(LogGraphAStarExample_HexGrid, Warning, TEXT("AHexGrid::CreateGrid(...) CreationStepDelegate not bound!")); 57 | } 58 | 59 | TileLayout = TLayout; 60 | 61 | Radius = GridRadius; 62 | 63 | for (int32 Q{ -Radius }; Q <= Radius; ++Q) 64 | { 65 | // Calculate R1 66 | int32 R1{ FMath::Max(-Radius, -Q - Radius) }; 67 | 68 | // Calculate R2 69 | int32 R2{ FMath::Min(Radius, -Q + Radius) }; 70 | 71 | for (int32 R{ R1 }; R <= R2; ++R) 72 | { 73 | FHCubeCoord CCoord{ FIntVector(Q, R, -Q - R) }; 74 | GridCoordinates.Add(CCoord); 75 | 76 | // If we provided a delegate execute it, with this we can make additional operations on each step of the loop, 77 | // in our example i use it in the blueprint to add a tile on each cube coordinate. 78 | CreationStepDelegate.ExecuteIfBound(TileLayout, CCoord); 79 | } 80 | } 81 | } 82 | 83 | 84 | FVector AHexGrid::HexToWorld(const FHCubeCoord &H) 85 | { 86 | // Set the layout orientation 87 | FHTileOrientation TileOrientation{}; 88 | if (TileLayout.TileOrientation == EHTileOrientationFlag::FLAT) 89 | { 90 | TileOrientation = HFlatTopLayout; 91 | } 92 | else 93 | { 94 | TileOrientation = HPointyLayout; 95 | } 96 | 97 | float x = ((TileOrientation.f0 * H.QRS.X) + (TileOrientation.f1 * H.QRS.Y)) * TileLayout.TileSize; 98 | float y = ((TileOrientation.f2 * H.QRS.X) + (TileOrientation.f3 * H.QRS.Y)) * TileLayout.TileSize; 99 | 100 | return FVector(x + TileLayout.Origin.X, y + TileLayout.Origin.Y, TileLayout.Origin.Z); 101 | } 102 | 103 | 104 | FHCubeCoord AHexGrid::WorldToHex(const FVector &Location) 105 | { 106 | // Set the layout orientation 107 | FHTileOrientation TileOrientation{}; 108 | if (TileLayout.TileOrientation == EHTileOrientationFlag::FLAT) 109 | { 110 | TileOrientation = HFlatTopLayout; 111 | } 112 | else 113 | { 114 | TileOrientation = HPointyLayout; 115 | } 116 | 117 | FVector InternalLocation{ FVector((Location.X - TileLayout.Origin.X) / TileLayout.TileSize, 118 | (Location.Y - TileLayout.Origin.Y) / TileLayout.TileSize, 119 | (Location.Z - TileLayout.Origin.Z)) // Z is useless here. 120 | }; 121 | 122 | float q = ((TileOrientation.b0 * InternalLocation.X) + (TileOrientation.b1 * InternalLocation.Y)); 123 | float r = ((TileOrientation.b2 * InternalLocation.X) + (TileOrientation.b3 * InternalLocation.Y)); 124 | 125 | FVector v{ (TileLayout.TileOrientation == EHTileOrientationFlag::FLAT) ? FVector(q, -q - r, r) : FVector(q, r, -q - r) }; 126 | 127 | return HexRound(FHFractional(v)); 128 | } 129 | 130 | 131 | FVector AHexGrid::SnapToGrid(const FVector &Location) 132 | { 133 | float TempZ{ Location.Z }; 134 | FVector Result{ HexToWorld(WorldToHex(Location)) }; 135 | Result.Z = TempZ; 136 | return Result; 137 | } 138 | 139 | 140 | FHCubeCoord AHexGrid::HexRound(const FHFractional &F) 141 | { 142 | int32 q{ int32(FMath::RoundToDouble(F.QRS.X)) }; 143 | int32 r{ int32(FMath::RoundToDouble(F.QRS.Y)) }; 144 | int32 s{ int32(FMath::RoundToDouble(F.QRS.Z)) }; 145 | 146 | float q_diff{ FMath::Abs(q - F.QRS.X) }; 147 | float r_diff{ FMath::Abs(r - F.QRS.Y) }; 148 | float s_diff{ FMath::Abs(s - F.QRS.Z) }; 149 | 150 | if ((q_diff > r_diff) && (q_diff > s_diff)) 151 | { 152 | q = -r - s; 153 | } 154 | else if (r_diff > s_diff) 155 | { 156 | r = -q - s; 157 | } 158 | else 159 | { 160 | s = -q - r; 161 | } 162 | 163 | return FHCubeCoord{ FIntVector(q, r, s) }; 164 | } 165 | 166 | 167 | bool AHexGrid::HexEqual(const FHCubeCoord &A, const FHCubeCoord &B) 168 | { 169 | return A == B; 170 | } 171 | 172 | FHCubeCoord AHexGrid::GetDirection(int32 Dir) 173 | { 174 | check(Dir < HDirections.Directions.Num()); 175 | return HDirections.Directions[Dir]; 176 | } 177 | 178 | FHCubeCoord AHexGrid::GetNeighbor(const FHCubeCoord &H, const FHCubeCoord &Dir) 179 | { 180 | return H + Dir; 181 | } 182 | 183 | -------------------------------------------------------------------------------- /Source/GraphAStarExample/Public/AI/GraphAStarNavMesh.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "NavMesh/RecastNavMesh.h" 7 | #include "GraphAStarNavMesh.generated.h" 8 | 9 | DECLARE_LOG_CATEGORY_EXTERN(LogGraphAStarExample_NavMesh, Log, All); 10 | 11 | DECLARE_CYCLE_STAT(TEXT("Hex Grid A* Pathfinding"), STAT_Navigation_HGASPathfinding, STATGROUP_Navigation); 12 | 13 | /** 14 | * TQueryFilter (FindPath's parameter) filter class is what decides which graph edges can be used and at what cost. 15 | */ 16 | struct FGridPathFilter 17 | { 18 | FGridPathFilter(const AGraphAStarNavMesh &InNavMeshRef) : NavMeshRef(InNavMeshRef) {} 19 | 20 | /** 21 | * Used as GetHeuristicCost's multiplier 22 | */ 23 | float GetHeuristicScale() const; 24 | 25 | /** 26 | * Estimate of cost from StartNodeRef to EndNodeRef 27 | */ 28 | float GetHeuristicCost(const int32 StartNodeRef, const int32 EndNodeRef) const; 29 | 30 | /** 31 | * Real cost of traveling from StartNodeRef directly to EndNodeRef 32 | */ 33 | float GetTraversalCost(const int32 StartNodeRef, const int32 EndNodeRef) const; 34 | 35 | /** 36 | * Whether traversing given edge is allowed 37 | */ 38 | bool IsTraversalAllowed(const int32 NodeA, const int32 NodeB) const; 39 | 40 | /** 41 | * Whether to accept solutions that do not reach the goal 42 | */ 43 | bool WantsPartialSolution() const; 44 | 45 | protected: 46 | 47 | /** 48 | * A reference to our NavMesh 49 | */ 50 | const AGraphAStarNavMesh &NavMeshRef; 51 | }; 52 | 53 | 54 | // We inherit this struct because we need a custom GetCost/GetLength 55 | struct FHexNavMeshPath : public FNavMeshPath 56 | { 57 | FORCEINLINE 58 | virtual float GetCostFromIndex(int32 PathPointIndex) const override 59 | { 60 | return CurrentPathCost; 61 | } 62 | 63 | FORCEINLINE 64 | virtual float GetLengthFromPosition(FVector SegmentStart, uint32 NextPathPointIndex) const override 65 | { 66 | // We exclude the starting point so -1 67 | return PathPoints.Num() - 1; 68 | } 69 | 70 | float CurrentPathCost{ 0 }; 71 | }; 72 | 73 | /** 74 | * 75 | */ 76 | UCLASS() 77 | class GRAPHASTAREXAMPLE_API AGraphAStarNavMesh : public ARecastNavMesh 78 | { 79 | GENERATED_BODY() 80 | 81 | public: 82 | 83 | /** 84 | * the function is static for a reason, (wiki copy-paste->) 85 | * comments in the code explain it's for performance reasons: Epic are concerned 86 | * that if a lot of agents call the pathfinder in the same frame the virtual call overhead will accumulate and take too long, 87 | * so instead the function is declared static and stored in the FindPathImplementation function pointer. 88 | * Which means you need to manually set the function pointer in your new navigation class constructor 89 | * (or in some other function like we do here in SetHexGrid(). 90 | */ 91 | static FPathFindingResult FindPath(const FNavAgentProperties &AgentProperties, const FPathFindingQuery &Query); 92 | 93 | /* Set a pointer to an hexagonal grid, it can be nullptr */ 94 | UFUNCTION(BlueprintCallable, Category = "GraphAStarExample|NavMesh") 95 | void SetHexGrid(class AHexGrid *HGrid); 96 | 97 | ////////////////////////////////////////////////////////////////////////// 98 | /** 99 | * Generic graph A* implementation 100 | * TGraph holds graph representation.Needs to implement functions : 101 | */ 102 | 103 | /* Type used as identification of nodes in the graph */ 104 | typedef int32 FNodeRef; 105 | 106 | /* Returns number of neighbors that the graph node identified with NodeRef has */ 107 | int32 GetNeighbourCount(FNodeRef NodeRef) const; 108 | 109 | /* Returns whether given node identification is correct */ 110 | bool IsValidRef(FNodeRef NodeRef) const; 111 | 112 | /* Returns neighbor ref */ 113 | FNodeRef GetNeighbour(const FNodeRef NodeRef, const int32 NeiIndex) const; 114 | ////////////////////////////////////////////////////////////////////////// 115 | 116 | 117 | /* Just a pointer to an hexagonal grid actor */ 118 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "GraphAStarExample|NavMesh") 119 | class AHexGrid *HexGrid; 120 | 121 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "GraphAStarExample|NavMesh") 122 | float PathPointZOffset{0.f}; 123 | }; 124 | 125 | -------------------------------------------------------------------------------- /Source/GraphAStarExample/Public/AI/HGAIController.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "AIController.h" 7 | #include "HGAIController.generated.h" 8 | 9 | /** 10 | * This is the parent class of BP_AIController_Example_B 11 | * 12 | * We need this inherited class just only to set and use our custom HGPathFollowingComponent 13 | * (look at the constructor in .cpp) 14 | * 15 | */ 16 | UCLASS() 17 | class GRAPHASTAREXAMPLE_API AHGAIController : public AAIController 18 | { 19 | GENERATED_BODY() 20 | 21 | public: 22 | 23 | AHGAIController(const FObjectInitializer &ObjectInitializer = FObjectInitializer::Get()); 24 | }; 25 | -------------------------------------------------------------------------------- /Source/GraphAStarExample/Public/AI/HGPathFollowingComponent.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Navigation/PathFollowingComponent.h" 7 | #include "GraphAstarNavMesh.h" 8 | #include "HGPathFollowingComponent.generated.h" 9 | 10 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnActorBumpDelegate, const FVector&, BumpLocation); 11 | 12 | /** 13 | * We inherit from the UPathFollowingComponent and we override a bunch of functions 14 | * just to let you know this exist and it can been very powerful. 15 | */ 16 | UCLASS() 17 | class GRAPHASTAREXAMPLE_API UHGPathFollowingComponent : public UPathFollowingComponent 18 | { 19 | GENERATED_BODY() 20 | 21 | 22 | 23 | /** called when moving agent collides with another actor */ 24 | virtual void OnActorBump(AActor *SelfActor, AActor *OtherActor, FVector NormalImpulse, const FHitResult &Hit) override; 25 | 26 | public: 27 | 28 | virtual void BeginPlay() override; 29 | 30 | /** 31 | * Executed if a "Bump" happen, we bind this delegate on the activation of our Behavior Tree Service BTS_BindBump 32 | */ 33 | UPROPERTY(BlueprintAssignable, Category = "GraphAStarExample|PathFollowingComponent") 34 | FOnActorBumpDelegate OnActorBumped; 35 | 36 | /** 37 | * Toggle our debug drawing. 38 | */ 39 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GraphAStarExample|PathFollowingComponent") 40 | bool DrawDebug{}; 41 | 42 | /** 43 | * The PathFollowingComponent has a pointer to the ANavigationData class but it isn't expose to Blueprint, 44 | * so we will expose it casted to our AGraphAStarNavMesh in the constructor. 45 | */ 46 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "GraphAStarExample|PathFollowingComponent") 47 | AGraphAStarNavMesh *GraphAStarNavMesh; 48 | 49 | protected: 50 | 51 | /** follow current path segment */ 52 | virtual void FollowPathSegment(float DeltaTime) override; 53 | }; 54 | -------------------------------------------------------------------------------- /Source/GraphAStarExample/Public/HexGrid/HGTypes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "HGTypes.generated.h" 5 | 6 | /** 7 | * Orientation of the tile 8 | * @see https://www.redblobgames.com/grids/hexagons/#basics 9 | */ 10 | UENUM(BlueprintType) 11 | enum class EHTileOrientationFlag : uint8 12 | { 13 | FLAT, 14 | POINTY, 15 | NONE 16 | }; 17 | 18 | 19 | /** 20 | * Information about the tile orientation, size and origin. 21 | * @see https://www.redblobgames.com/grids/hexagons/implementation.html#layout 22 | */ 23 | USTRUCT(BlueprintType) 24 | struct FHTileLayout 25 | { 26 | GENERATED_USTRUCT_BODY() 27 | 28 | FHTileLayout() {} 29 | 30 | FHTileLayout(EHTileOrientationFlag orientation, float size, FVector origin) 31 | : TileOrientation(orientation), TileSize(size), Origin(origin) 32 | {} 33 | 34 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GraphAStarExample|HGTypes|Layout") 35 | EHTileOrientationFlag TileOrientation { 36 | EHTileOrientationFlag::NONE 37 | }; 38 | 39 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GraphAStarExample|HGTypes|Layout") 40 | float TileSize{ 0 }; 41 | 42 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GraphAStarExample|HGTypes|Layout") 43 | FVector Origin { 44 | FVector::ZeroVector 45 | }; 46 | }; 47 | 48 | /** 49 | * @see https://www.redblobgames.com/grids/hexagons/#coordinates 50 | * @see https://www.redblobgames.com/grids/hexagons/implementation.html#hex 51 | */ 52 | USTRUCT(BlueprintType) 53 | struct FHAxialCoord 54 | { 55 | GENERATED_USTRUCT_BODY() 56 | 57 | FHAxialCoord() {} 58 | 59 | FHAxialCoord(int32 q, int32 r) 60 | { 61 | QR.X = q; 62 | QR.Y = r; 63 | } 64 | 65 | FHAxialCoord(FIntPoint _qr) : QR(_qr) 66 | { 67 | } 68 | 69 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GraphAStarExample|HGTypes|Axial Coord") 70 | FIntPoint QR {FIntPoint::ZeroValue}; 71 | 72 | }; 73 | 74 | /** 75 | * @see https://www.redblobgames.com/grids/hexagons/#coordinates 76 | * @see https://www.redblobgames.com/grids/hexagons/implementation.html#hex 77 | */ 78 | USTRUCT(BlueprintType) 79 | struct FHCubeCoord 80 | { 81 | GENERATED_USTRUCT_BODY() 82 | 83 | FHCubeCoord() {} 84 | 85 | FHCubeCoord(int32 q, int32 r, int32 s) 86 | { 87 | check(q + r + s == 0); 88 | QRS.X = q; 89 | QRS.Y = r; 90 | QRS.Z = s; 91 | } 92 | 93 | FHCubeCoord(FIntVector _v) : QRS(_v) {} 94 | 95 | 96 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GraphAStarExample|HGTypes|Cube Coord") 97 | FIntVector QRS { 98 | FIntVector::ZeroValue 99 | }; 100 | 101 | 102 | friend FHCubeCoord operator+(const FHCubeCoord &lhs, const FHCubeCoord &rhs) 103 | { 104 | return FHCubeCoord{ lhs.QRS + rhs.QRS }; 105 | } 106 | 107 | friend FHCubeCoord operator-(const FHCubeCoord &lhs, const FHCubeCoord &rhs) 108 | { 109 | return FHCubeCoord{ lhs.QRS - rhs.QRS }; 110 | } 111 | 112 | friend FHCubeCoord operator*(const FHCubeCoord &lhs, int32 k) 113 | { 114 | return FHCubeCoord{ lhs.QRS * k }; 115 | } 116 | 117 | friend bool operator==(const FHCubeCoord &lhs, const FHCubeCoord &rhs) 118 | { 119 | return lhs.QRS == rhs.QRS; 120 | } 121 | 122 | friend bool operator!=(const FHCubeCoord &lhs, const FHCubeCoord &rhs) 123 | { 124 | return lhs.QRS != rhs.QRS; 125 | } 126 | 127 | friend uint32 GetTypeHash(const FHCubeCoord &Other) 128 | { 129 | FString TypeHash{ Other.QRS.ToString() }; 130 | return GetTypeHash(TypeHash); 131 | } 132 | }; 133 | 134 | /** 135 | * @see https://www.redblobgames.com/grids/hexagons/implementation.html#fractionalhex 136 | */ 137 | USTRUCT(BlueprintType) 138 | struct FHFractional 139 | { 140 | GENERATED_USTRUCT_BODY() 141 | 142 | FHFractional() {} 143 | 144 | FHFractional(float q, float r, float s) 145 | { 146 | QRS.X = q; 147 | QRS.Y = r; 148 | QRS.Z = s; 149 | } 150 | 151 | FHFractional(FVector _v) : QRS(_v) 152 | {} 153 | 154 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GraphAStarExample|HGTypes|Fractional Coord") 155 | FVector QRS {FVector::ZeroVector}; 156 | }; 157 | 158 | /** 159 | * @see https://www.redblobgames.com/grids/hexagons/#neighbors 160 | */ 161 | USTRUCT(BlueprintType) 162 | struct FHDirections 163 | { 164 | GENERATED_USTRUCT_BODY() 165 | 166 | FHDirections() 167 | { 168 | // Flat | Pointy 169 | Directions.Add(FHCubeCoord(FIntVector(0, 1, -1))); // 0 Top | Top Left 170 | Directions.Add(FHCubeCoord(FIntVector(1, 0, -1))); // 1 Top Right | Top Right 171 | Directions.Add(FHCubeCoord(FIntVector(1, -1, 0))); // 2 Bottom Right | Right 172 | Directions.Add(FHCubeCoord(FIntVector(0, -1, 1))); // 3 Bottom | Bottom Right 173 | Directions.Add(FHCubeCoord(FIntVector(-1, 0, 1))); // 4 Bottom Left | Bottom Left 174 | Directions.Add(FHCubeCoord(FIntVector(-1, 1, 0))); // 5 Top Left | Left 175 | } 176 | 177 | UPROPERTY(BlueprintReadOnly, Category = "GraphAStarExample|HGTypes|Directions") 178 | TArray Directions; 179 | }; 180 | 181 | /** 182 | * @see https://www.redblobgames.com/grids/hexagons/#neighbors 183 | */ 184 | USTRUCT(BlueprintType) 185 | struct FHDiagonals 186 | { 187 | GENERATED_USTRUCT_BODY() 188 | 189 | FHDiagonals() 190 | { 191 | // Flat | Pointy 192 | Diagonals.Add(FHCubeCoord(FIntVector(1, 1, -2))); // 0 Top Right | Top 193 | Diagonals.Add(FHCubeCoord(FIntVector(2, -1, -1))); // 1 Right | Top Right 194 | Diagonals.Add(FHCubeCoord(FIntVector(1, -2, 1))); // 2 Bottom Right | Bottom Right 195 | Diagonals.Add(FHCubeCoord(FIntVector(-1, -1, 2))); // 3 Bottom Left | Bottom 196 | Diagonals.Add(FHCubeCoord(FIntVector(-2, 1, 1))); // 4 Left | Bottom Left 197 | Diagonals.Add(FHCubeCoord(FIntVector(-1, 2, -1))); // 5 Top Left | Top Left 198 | } 199 | 200 | UPROPERTY(BlueprintReadOnly, Category = "GraphAStarExample|HGTypes|Diagonals") 201 | TArray Diagonals; 202 | }; 203 | 204 | /** 205 | * @see https://www.redblobgames.com/grids/hexagons/implementation.html#layout 206 | */ 207 | USTRUCT(BlueprintType) 208 | struct FHTileOrientation 209 | { 210 | GENERATED_USTRUCT_BODY() 211 | 212 | FHTileOrientation() 213 | { 214 | } 215 | 216 | friend bool operator==(const FHTileOrientation &lhs, const FHTileOrientation &rhs) 217 | { 218 | return (lhs.f0 == rhs.f0) && (lhs.f1 == rhs.f1) && (lhs.f2 == rhs.f2) && (lhs.f3 == rhs.f3); 219 | } 220 | 221 | double f0, f1, f2, f3; // f0, f1 for X, f2, f3 for Y - used in HexToWorld 222 | double b0, b1, b2, b3; // Inverse. Q b0*x, b1*y - R b2*x, b3*y - used in WorldToHex 223 | }; 224 | 225 | const struct FHFlatTopOrientation : FHTileOrientation 226 | { 227 | // Flat top hexagon layout (X = y, Y = -x where uppercase is the original coordinates from Red Blob Games article, lowercase is UE4 coordinates) 228 | FHFlatTopOrientation() 229 | { 230 | // UE4 | Original 231 | f0 = -FMath::Sqrt(3.0) / 2.0; // -f2 | f0 = 3/2 232 | f1 = -FMath::Sqrt(3.0); // -f3 | f1 = 0 233 | f2 = 3.0 / 2.0; // f0 | f2 = sqrt(3)/2 234 | f3 = 0.0; // f1 | f3 = sqrt(3) 235 | 236 | b0 = 0.0; // b1 | b0 = 2/3 237 | b1 = 2.0 / 3.0; // b0 | b1 = 0 238 | b2 = FMath::Sqrt(3.0) / 3.0; // b3 | b2 = -1/3 239 | b3 = -1.0 / 3.0; // b2 | b3 = sqrt(3)/3 240 | } 241 | 242 | }HFlatTopLayout; 243 | 244 | const struct FHPointyOrientation : FHTileOrientation 245 | { 246 | FHPointyOrientation() 247 | { 248 | // UE4 | Original 249 | f0 = 0.0; // -f2 | f0 = sqrt(3) 250 | f1 = -3.0 / 2.0; // -f3 | f1 = sqrt(3)/2 251 | f2 = FMath::Sqrt(3.0); // f0 | f2 = 0 252 | f3 = FMath::Sqrt(3.0) / 2.0; // f1 | f3 = 3/2 253 | 254 | b0 = 1.0 / 3.0; // -b1 | b0 = sqrt(3)/3 255 | b1 = FMath::Sqrt(3.0) / 3.0; // b0 | b1 = -1/3 256 | b2 = -2.0 / 3.0; // -b3 | b2 = 0 257 | b3 = 0.0; // b2 | b3 = 2/3 258 | } 259 | 260 | }HPointyLayout; 261 | 262 | 263 | 264 | 265 | 266 | -------------------------------------------------------------------------------- /Source/GraphAStarExample/Public/HexGrid/HexGrid.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | #include "HGTypes.h" 8 | #include "HexGrid.generated.h" 9 | 10 | DECLARE_LOG_CATEGORY_EXTERN(LogGraphAStarExample_HexGrid, Log, All); 11 | DECLARE_STATS_GROUP(TEXT("HEXGRID_STATS"), STATGROUP_HEXGRID, STATCAT_Advanced); 12 | DECLARE_CYCLE_STAT(TEXT("CreateGrid(..)"), STAT_CreateGrid, STATGROUP_HEXGRID); 13 | 14 | /* 15 | Just a rough implementation of a tile. 16 | */ 17 | USTRUCT(BlueprintType) 18 | struct FHexTile 19 | { 20 | GENERATED_USTRUCT_BODY() 21 | 22 | FHexTile() {}; 23 | 24 | /* Coordinate in Cube space */ 25 | UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "GraphAStarExample|HexGrid") 26 | FHCubeCoord CubeCoord; 27 | 28 | /* Coordinate in World space */ 29 | UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "GraphAStarExample|HexGrid") 30 | FVector WorldPosition { 31 | FVector::ZeroVector 32 | }; 33 | 34 | /* Cost of the tile, for a well execution of the GraphAStar pathfinder it need to have a value of at least 1 */ 35 | UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "GraphAStarExample|HexGrid", meta =(ClampMin = 1)) 36 | float Cost{}; 37 | 38 | /* Is this tile a blocking tile? For example a static obstacle. */ 39 | UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "GraphAStarExample|HexGrid") 40 | bool bIsBlocking{}; 41 | 42 | 43 | friend bool operator==(const FHexTile &A, const FHexTile &B) 44 | { 45 | return (A.CubeCoord == B.CubeCoord) && (A.Cost == B.Cost) && (A.bIsBlocking == B.bIsBlocking); 46 | } 47 | 48 | friend bool operator!=(const FHexTile &A, const FHexTile &B) 49 | { 50 | return !(A == B); 51 | } 52 | 53 | }; 54 | 55 | 56 | /* Delegate used in the CreateGrid function, executed if bound on each inner loop step. */ 57 | DECLARE_DYNAMIC_DELEGATE_TwoParams(FCreationStepDelegate, const FHTileLayout &, TileLayout, const FHCubeCoord &, Coord); 58 | 59 | 60 | /** 61 | * This is a very simple, rough and unoptimized implementation of an hexagonal grid, 62 | * i heavily suggest to read the awesome Red Blob Games article before look at it. 63 | * @see https://www.redblobgames.com/grids/hexagons/ 64 | */ 65 | UCLASS() 66 | class GRAPHASTAREXAMPLE_API AHexGrid : public AActor 67 | { 68 | GENERATED_BODY() 69 | 70 | public: 71 | // Sets default values for this actor's properties 72 | AHexGrid(); 73 | 74 | // Called every frame 75 | virtual void Tick(float DeltaTime) override; 76 | 77 | /** 78 | * Create a new grid and fill the CubeCoordinates array. 79 | * @param TLayout Tile layout structure. 80 | * @param GridRadius Radius of the grid in tiles. 81 | * @param CreationStepDelegate This parameter is optional, if bound is executed at each step. 82 | * @see https://www.redblobgames.com/grids/hexagons/implementation.html#map-shapes 83 | */ 84 | UFUNCTION(BlueprintCallable, Category = "GraphAStarExample|HexGrid", meta = (AutoCreateRefTerm = "CreationStepDelegate")) 85 | void CreateGrid(const FHTileLayout &TLayout, const int32 GridRadius, const FCreationStepDelegate &CreationStepDelegate); 86 | 87 | /** 88 | * Convert coordinates from Cube space to World space. 89 | * @see https://www.redblobgames.com/grids/hexagons/#hex-to-pixel 90 | */ 91 | UFUNCTION(BlueprintCallable, Category = "GraphAStarExample|HexGrid") 92 | FVector HexToWorld(const FHCubeCoord &H); 93 | 94 | /** 95 | * Convert coordinates from World space to Cube space. 96 | * @see https://www.redblobgames.com/grids/hexagons/#pixel-to-hex 97 | */ 98 | UFUNCTION(BlueprintCallable, Category = "GraphAStarExample|HexGrid") 99 | FHCubeCoord WorldToHex(const FVector &Location); 100 | 101 | /** Snap a World coordinate to the Grid space. */ 102 | UFUNCTION(BlueprintCallable, Category = "GraphAStarExample|HexGrid") 103 | FVector SnapToGrid(const FVector &Location); 104 | 105 | /** 106 | * Round from floating-point cube coordinate to integer cube coordinate. 107 | * @see https://www.redblobgames.com/grids/hexagons/#rounding 108 | */ 109 | UFUNCTION(BlueprintCallable, Category = "GraphAStarExample|HexGrid") 110 | FHCubeCoord HexRound(const FHFractional &F); 111 | 112 | /** 113 | * Compare two Cube coordinate. 114 | * @see https://www.redblobgames.com/grids/hexagons/implementation.html#hex-equality 115 | */ 116 | UFUNCTION(BlueprintCallable, Category = "GraphAStarExample|HexGrid") 117 | bool HexEqual(const FHCubeCoord &A, const FHCubeCoord &B); 118 | 119 | /** 120 | * Return one of the six cube directions. 121 | * @see https://www.redblobgames.com/grids/hexagons/#neighbors 122 | */ 123 | UFUNCTION(BlueprintCallable, Category = "GraphAStarExample|HexGrid") 124 | FHCubeCoord GetDirection(int32 Dir); 125 | 126 | /** 127 | * Return the neighbor Cube coordinate in the provided direction. 128 | * @see https://www.redblobgames.com/grids/hexagons/#neighbors 129 | */ 130 | UFUNCTION(BlueprintCallable, Category = "GraphAStarExample|HexGrid") 131 | FHCubeCoord GetNeighbor(const FHCubeCoord &H, const FHCubeCoord &Dir); 132 | 133 | /** Array of HexTiles, in our example we fill it in blueprint with the CreationStepDelegate. */ 134 | UPROPERTY(BlueprintReadWrite, Category = "GraphAStarExample|HexGrid") 135 | TArray GridTiles; 136 | 137 | /** Array of Cube coordinates that compose the grid. */ 138 | UPROPERTY(BlueprintReadWrite, Category = "GraphAStarExample|HexGrid") 139 | TArray GridCoordinates{}; 140 | 141 | /** 142 | * Layout of the tile (i know is very misleading, please read the article) 143 | * @see https://www.redblobgames.com/grids/hexagons/implementation.html#layout 144 | */ 145 | UPROPERTY(BlueprintReadWrite, Category = "GraphAStarExample|HexGrid") 146 | FHTileLayout TileLayout {}; 147 | 148 | /** 149 | * Radius of the grid in "tiles", clamped [1, 25] 150 | */ 151 | UPROPERTY(BlueprintReadWrite, Category = "GraphAStarExample|HexGrid") 152 | int32 Radius {}; 153 | 154 | protected: 155 | 156 | // Called when the game starts or when spawned 157 | virtual void BeginPlay() override; 158 | 159 | 160 | private: 161 | 162 | FHDirections HDirections{}; 163 | }; 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /Source/GraphAStarExampleEditor.Target.cs: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | public class GraphAStarExampleEditorTarget : TargetRules 7 | { 8 | public GraphAStarExampleEditorTarget( TargetInfo Target) : base(Target) 9 | { 10 | Type = TargetType.Editor; 11 | 12 | ExtraModuleNames.AddRange( new string[] { "GraphAStarExample" } ); 13 | } 14 | } 15 | --------------------------------------------------------------------------------