Jump to content

Search the Community

Showing results for tags 'ai'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Blogs

  • Development Blog
  • Canardian's Blog
  • Beyond Civilization Blog Ext
  • Tyler's Blog
  • macklebee's Blog
  • B-Byrnes' Blog
  • Marleys Ghost's Blog
  • omid3098's Blog
  • Richardsimpo123456's Blog
  • DJDD's Blog
  • Rick's Blog
  • --
  • xtreampb's Blog
  • Economically Disadvantaged
  • klepto2's Blog
  • Old Blog
  • Chris Paulson's Blog
  • Davaris' Blog
  • nil
  • Rekindled Phoenix's Blog
  • Flexman's Blog
  • Kattemaksu Online
  • Marcousik's Creations Blog
  • EVE LBS Studio
  • Rendering puzzles
  • Screen Size Utility
  • Niosop's Blog
  • 1299 RTS Game Project
  • Blitzbat's Blog
  • gordonramp's Blog
  • Andy Gilbert's Blog
  • Marcus' Blog
  • JornAggror Blog
  • diedir's Blog
  • wayneg's Blog
  • Masterxilo's Blog
  • Dave Lee's Blog
  • peubuy's blog
  • OD Arts -Leadwerks Blog
  • The progression......
  • Leadwerks News
  • The Progression
  • Pancake's Blog
  • Test Blog
  • BLaBZ Blog
  • Pure³d's Blog
  • dreamhead Blog
  • RVL's Blog
  • DB's Blog
  • Toxin Games Development Blog
  • CombatHelo Blog (RSS Import)
  • Foolish's Blog
  • ZioRed's Corner
  • Toxin Games Twitter
  • Shard - Third Initiative
  • Shard's Blog
  • Soamp's Blog
  • Soamp's Blog
  • Laurens' Blog
  • JT`s warehouse district
  • Kennar's Blog
  • KiteFuchs' Blog
  • KiteFuchs' Blog
  • Rachel's Dev-Blog
  • Eagle's Blog
  • zaphos' Blog
  • please delete this blog
  • Afke's Blog
  • Richard Simpson
  • knowledgegranted's Blog
  • EdzUp(GD)'s Blog
  • aGameLife's Kelasel MMORPG Blog
  • tournamentdan's Blog
  • Beyond Civilization Blog
  • smashthewindow
  • AnniXa's Blog
  • AnniXa's Blog
  • Elemental Development
  • DigitalHax Blog
  • Aria's Blog
  • Strogg76's Blog
  • Clackdor's Blog
  • Leadwerks Project Status
  • simpleprogrammer's Blog
  • Pathfinding in LE.2.x
  • Naughty Alien's Blog
  • smashthewindow's Blog
  • Ali Salehi's Blog
  • Jardar's Blog
  • Chris Vossen's Development Blog
  • Scarlet Thread Studios' Blog
  • Kronos' Blog
  • Benton's Blog
  • ChrisV's Blog
  • tjheldna's Blog
  • shadmar's Blog
  • 3D Masons, LLC
  • Andy Gilbert's Enviro Models
  • ParaToxic's Blog
  • NarkLord's Blog
  • ChrisMAN's Blog
  • ChrisMAN's Blog
  • CGMan's Blog
  • The Game
  • Chris Tutorials
  • Leadwerks 3 Experience
  • An Alien Saga
  • klepto2 & Leadwerks 3
  • Inside the mind of eternal insomniac
  • josk's Blog
  • Shader Development in Leadwerks 3.1
  • 3D Coat : Column game making of
  • Einlander's Blog
  • Ginger George's Blog
  • I have a problem
  • Michael_J's Blog
  • nasamydifol's Blog
  • Digman's Blog
  • noesisGUI
  • SavageDogg38's Blog
  • Built from Ruins
  • Remaining Days
  • test_external_blog
  • The Hunt For Food Blog
  • Crazy Minnow Studio
  • CrazyMinnowStudio
  • Guppy's Blog
  • Playing Online
  • Evayr's Blog
  • DerRidda's Blog
  • Karl's Blog
  • whiterabbit's Blog
  • Tinyboss Games
  • abendkleider's Blog
  • sacguccireplica's Blog
  • Arena
  • Imchasinyou's Blog
  • xtom's Blog
  • Lua is better than you think.
  • mdgunn's Blog
  • Crime Closer
  • lxFirebal69xl's Blog
  • Wedmer's Blog
  • Lockdown, going forward.
  • Pump-Action Captain
  • Igor's Blog
  • Dead Anyway
  • Runenrise and more
  • reepblue's Blog
  • Slippy's Corner
  • LUA Musings
  • severjack's Blog
  • mikeporter's Blog
  • miko93's Blog
  • Megalocerous' Blog
  • lxFirebal69xl's Blog
  • aiaf's Blog
  • Lostghbear's Blog
  • echo $BLOG_NAME > blog_title.tmpl
  • johnadam111's Blog
  • Dwarf Beard
  • burgelkat's Blog
  • Charrua's Blog
  • peterpaul's Blog
  • joshmathews' Blog
  • Martin Kearl's Blog
  • Brutile's blog
  • tipforeveryone's Blog
  • Glushchenko Blog
  • Express Lab Games Blog
  • Blueapples' Blog
  • DooMAGE's Blog
  • Structura devblog
  • UltraEngine - Experiences, add-ons and other Stuff
  • martyj's Blog
  • Dragonfreak's Blog
  • Brutile's Blog
  • assigmenthelp
  • Case Study Assignment Help
  • How to deal with bad grade ?
  • How to deal with bad grade ?
  • GameDev Blog
  • devcjohnson's Blog
  • devcjohnson's Blog
  • THE WHAT? Blog
  • The Demurian Scribe
  • GUI Editor
  • GUI Tutorial
  • noob_shaders
  • The Seventh World
  • Phodex Games Blog
  • Leadwerks VS Source 2
  • Work in Progress - Scifi PBR Media
  • [C++] First Player game start
  • The Blog of Yue
  • Snowboarding Development Blog
  • Ocean: Rendering in Leadwerks 4
  • Game Ready Maps
  • Ultra App Kit (Advanced Custom Widgets)
  • Poking around
  • Blender tutorials
  • Usefull Scripts & Components
  • Thirsty Panther
  • Ultimate Action Game Controller
  • Ultra Tutorials

Forums

  • Software
    • General Discussion
    • Programming
    • Game Artwork
    • Addon Development
    • Showcase
    • Suggestion Box
    • Bug Reports
  • Addons

Categories

  • Streams and Events
  • Tutorials
  • Games
  • Work in Progress

Categories

  • Components
  • Effects
  • Extensions
  • Materials
    • Asphalt
    • Brick
    • Concrete
    • Debris
    • Effects
    • Fabric
    • Ground
    • Marble
    • Metal
    • Organic
    • Plaster
    • Rock
    • Roof
    • Sand
    • Snow
    • Surface Imperfections
    • Tile
    • Wood
  • Models
    • Animals & Creatures
    • Architecture
    • Food
    • Machinery
    • Plants and Vegetation
    • Props
    • Vehicles
    • Tools
  • Scenes
  • Skyboxes
  • Sound
  • Tools

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Location

Found 23 results

  1. In this tutorial we will make a simple real-time tactic/strategy game in Ultra Engine. Plan: 1. Download and import ground material, 2 characters models with animations 2. Create Unit component with bot behavior. 3. Add control over player units. 4. Making prefabs and new map In this tutorial used 2038 Build from Steam Beta branch (Dev in Standalone version). Build number can be found in the Editor Help->About Asset import Download and unpack ground material from there in Materials\Ground folder Make in Models folder Characters in it Warrok and Paladin subfolders. Now login into https://www.mixamo.com/ Find there Paladin character and download with these settings without animations to get T-Pose as standard bind pose: Now we need animations from the Sword And Shield series (type Shield into the search field on top to filter them): Idle, Run, lash, Impact, Death. Hit "In Place" Checkbox to avoid character offset while animations: Put these .fbx to Paladin folder. Convert them into .mdl with right click in project tab of the Editor if there were not already If material was not created correctly then take pngs textures from "Paladin WProp J Nordstrom.fbm" folder which was created by converted and put in Paladin folder Convert them to DDS with Right Click. You can delete png and fbx files now. Now Paladin should look textured. Rename "Paladin WProp J Nordstrom" intro "Paladin" just for convenience. Open Paladin.mdl with double click to open Model Viewer. Let's load animations into this model from models with animations ("Sword And Shield Idle.mdl" etc): We need to rename those animations to use them properly later - open the Model tab, select animation to rename. You can play it to find out which is what. Lower will appear Animation panel where you can click on name to rename it: Let's call these animations: Idle, Attack, Pain, Death. There is also animation with T-Pose which can be deleted with Tools->Remove Sequence. We don't need animations files anymore and Paladin folder content should looks like that: One more thing needs to be done for model - Collider. In View toggle Show Collider, open Model tab, select SKIN_MESH and in Physics choose Cylinder collider. Offset and Size settings: We need worth enemy for our paladins - download Warrok model and animations from Mutant series - Idle, Run, Dying, Punch. For pain i chose "Standing React Large From Right" animation. Next do the same stuff as it was with Paladin - material, animations etc. In Transform make scale 0.85 so it would match Paladin size. Collider settings: Unit component After preparing character models we now need a component which will have: Unit params: health, speed, damage etc. Playing animations Bot behaviour - move and attack nearby enemies Input for player to move and attack something specific Create in "Source\Components\AI" Unit.json, Unit.h, Unit.cpp files and include last two into project. Unit.json: { "component": { "properties": [ { "name": "enabled", "label": "Enabled", "value": true }, { "name": "isFullPlayerControl", "label": "Full Player Control", "value": false }, { "name": "isPlayer", "label": "Is Player Unit", "value": false }, { "name": "team", "label": "Team", "value": 1, "options": [ "Neutral", "Good", "Bad" ] }, { "name": "health", "label": "Health", "value": 100 }, { "name": "maxHealth", "label": "Max Health", "value": 100 }, { "name": "speed", "label": "Speed", "value": 3.0 }, { "name": "attackRange", "label": "Attack Range", "value": 2.0 }, { "name": "attackDamage", "label": "Attack Damage", "value": 30 }, { "name": "attackFrame", "label": "Attack Frame", "value": 5 }, { "name": "painCooldown", "label": "Pain Cooldown", "value": 1000 }, { "name": "decayTime", "label": "Decay Time", "value": 10000 }, { "name": "target", "label": "Target", "value": null }, { "name": "targetPoint", "label": "Target Point", "value": null }, { "name": "attackName", "label": "Attack Name", "value": "Attack" }, { "name": "idleName", "label": "Idle Name", "value": "Idle" }, { "name": "painName", "label": "Pain", "value": "Pain" }, { "name": "deathName", "label": "Death", "value": "Death" }, { "name": "runName", "label": "Run", "value": "Run" } ], "inputs": [ { "name": "Enable" }, { "name": "Disable" } ] } } Parameters descriptions can be found in Unit.h: #pragma once #include "UltraEngine.h" #include "../BaseComponent.h" using namespace UltraEngine; //abstract class which will be a parent for other units classes such as Beast and Hunter //partly based of Enemy/Monster/Player default classes class Unit : public BaseComponent { protected: //so it could be added for entity with FPS Player component bool isFullPlayerControl = false; int health = 100; int maxHealth = 100; //used for AI navigation, weak_ptr just to make sure that component will not keep it if stays after map unload somehow std::weak_ptr<NavMesh> navMesh; //unique per entity so shared_ptr //NavAgent used to create to plot navigation paths in NavMesh std::shared_ptr<NavAgent> agent; //how far AI see its enemies in meters float perceptionRadius = 10; //how long to pursue when out of radius float chaseMaxDistance = perceptionRadius * 2; //is target a priority bool isForcedTarget = false; //target to follow and attack if possible std::weak_ptr<Entity> targetWeak; //to avoid fighting bool isForcedMovement = false; //which distance to point should be to reach it float targetPointDistance = 0.5f; //place to reach std::shared_ptr<Entity> targetPoint; //is attack animation playing bool isAttacking = false; //when attack started uint64_t meleeAttackTime = 0; //do damage in meleeAttackTiming after attack start int attackFrame = 5; float attackRange = 2.0f; int attackDamage = 30; //pain/git hit state bool isInPain = false; //can't start new pain animation immediately to avoid infinite stugger int painCooldown = 300; //when pain animation started uint64_t painCooldownTime; //how fast unit is float speed = 3.0; //when to try scan again uint64_t nextScanForTargetTime = 0ULL;//unsigned long long //animations names WString attackName; WString idleName; WString painName; WString deathName; WString runName; //health bar above unit shared_ptr<Sprite> healthBar; shared_ptr<Sprite> healthBarBackground; bool isSelected = false; //to keep camera pointer for unit health bars std::weak_ptr<Camera> cameraWeak; //to be able to remove entity inside of component later std::weak_ptr<Scene> sceneWeak; //time in ms before delete model after a death, 0 of disabled int decayTime = 10000; shared_ptr<Timer> removeEntityTimer; static bool RemoveEntityCallback(const UltraEngine::Event& ev, shared_ptr<UltraEngine::Object> extra); virtual void scanForTarget(); bool goTo(); //pick filter static bool RayFilter(shared_ptr<Entity> entity, shared_ptr<Object> extra); //attack target if in range static void AttackHook(shared_ptr<Skeleton> skeleton, shared_ptr<Object> extra); //disable attacking state static void EndAttackHook(shared_ptr<Skeleton> skeleton, shared_ptr<Object> extra); //disable pain state static void EndPainHook(shared_ptr<Skeleton> skeleton, shared_ptr<Object> extra); public: int team = 0;//0 neutral, 1 player team, 2 enemy bool isPlayer = false; Unit(); shared_ptr<Component> Copy() override; void Start() override; bool Load(table& t, shared_ptr<Stream> binstream, shared_ptr<Scene> scene, const LoadFlags flags, shared_ptr<Object> extra) override; bool Save(table& t, shared_ptr<Stream> binstream, shared_ptr<Scene> scene, const SaveFlags flags, shared_ptr<Object> extra) override; //deal a damage to this unit by attacker void Damage(const int amount, shared_ptr<Entity> attacker) override; //kill this unit by attacker void Kill(shared_ptr<Entity> attacker) override; bool isAlive(); void Update() override; bool isEnemy(int otherUnitTeam) const; void goTo(Vec3 positionToGo, bool isForced = false); void attack(shared_ptr<Entity> entityToAttack, bool isForced = false); void select(bool doSelect = true); }; #pragma once #include "UltraEngine.h" #include "Unit.h" #include "../Logic/WayPoint.h" using namespace UltraEngine; Unit::Unit() { name = "Unit"; attackName = "Attack"; idleName = "Idle"; painName = "Pain"; deathName = "Death"; runName = "Run"; } shared_ptr<Component> Unit::Copy() { return std::make_shared<Unit>(*this); } void Unit::Start() { auto entity = GetEntity(); auto model = entity->As<Model>(); //for custom save/load system entity->AddTag("Unit"); if (!isFullPlayerControl) { //checking efficiently if Unit have a nav mesh if (!navMesh.expired()) { //1 m radius because of Beast long model, 0.5 would better otherwise, 2 m height agent = CreateNavAgent(navMesh.lock(), 0.5, 2); agent->SetMaxSpeed(speed); agent->SetPosition(entity->GetPosition(true)); agent->SetRotation(entity->GetRotation(true).y); entity->SetPosition(0, 0, 0); //becase models rotated by back entity->SetRotation(0, 180, 0); entity->Attach(agent); } entity->SetCollisionType(COLLISION_PLAYER); entity->SetMass(0); entity->SetPhysicsMode(PHYSICS_RIGIDBODY); } if (model) { auto seq = model->FindAnimation(attackName); if (seq != -1) { int count = model->CountAnimationFrames(seq); //to disable attack state at end of attack animation model->skeleton->AddHook(seq, count - 1, EndAttackHook, Self()); //to deal damage to target at range at specific animation frame model->skeleton->AddHook(seq, attackFrame, AttackHook, Self()); } seq = model->FindAnimation(painName); if (seq != -1) { int count = model->CountAnimationFrames(seq); //to disable pain state at end of pain animation model->skeleton->AddHook(seq, count - 1, EndPainHook, Self()); } } if (!isFullPlayerControl) { int healthBarHeight = 5; healthBar = CreateSprite(entity->GetWorld(), maxHealth, healthBarHeight); if (team == 1) { healthBar->SetColor(0, 1, 0); } else { healthBar->SetColor(1, 0, 0); } healthBar->SetPosition(0, 0, 0.00001f); healthBar->SetRenderLayers(2); healthBarBackground = CreateSprite(entity->GetWorld(), maxHealth, healthBarHeight); healthBarBackground->SetColor(0.1f, 0.1f, 0.1f); //to put it behind health bar healthBarBackground->SetPosition(0, 0, 0.00002f); healthBarBackground->SetRenderLayers(2); } auto world = entity->GetWorld(); shared_ptr<Camera> camera; for (auto const& cameraEntity : world->GetTaggedEntities("Camera")) { camera = cameraEntity->As<Camera>(); break; } if (!camera) { for (auto const& cameraEntity : world->GetEntities()) { camera = cameraEntity->As<Camera>(); if (camera) { break; } } } cameraWeak = camera; BaseComponent::Start(); } bool Unit::Load(table& properties, shared_ptr<Stream> binstream, shared_ptr<Scene> scene, const LoadFlags flags, shared_ptr<Object> extra) { sceneWeak = scene; if (properties["isFullPlayerControl"].is_boolean()) isFullPlayerControl = properties["isFullPlayerControl"]; if (properties["isPlayer"].is_boolean()) isPlayer = properties["isPlayer"]; if (properties["team"].is_number()) team = properties["team"]; if (properties["health"].is_number()) health = properties["health"]; if (properties["maxHealth"].is_number()) maxHealth = properties["maxHealth"]; if (properties["attackDamage"].is_number()) attackDamage = properties["attackDamage"]; if (properties["attackRange"].is_number()) attackRange = properties["attackRange"]; if (properties["attackFrame"].is_number()) attackFrame = properties["attackFrame"]; if (properties["painCooldown"].is_number()) painCooldown = properties["painCooldown"]; if (properties["enabled"].is_boolean()) enabled = properties["enabled"]; if (properties["decayTime"].is_number()) decayTime = properties["decayTime"]; if (properties["attackName"].is_string()) attackName = properties["attackName"]; if (properties["idleName"].is_string()) idleName = properties["idleName"]; if (properties["deathName"].is_string()) deathName = properties["deathName"]; if (properties["painName"].is_string()) painName = properties["painName"]; if (properties["runName"].is_string()) runName = properties["runName"]; if (properties["target"].is_string()) { std::string id = properties["target"]; targetWeak = scene->GetEntity(id); } else { targetWeak.reset(); } if (properties["targetPoint"].is_string()) { std::string id = properties["targetPoint"]; targetPoint = scene->GetEntity(id); } else { targetPoint = nullptr; } if (properties["isForcedMovement"].is_boolean()) isForcedMovement = properties["isForcedMovement"]; if (properties["position"].is_array() && properties["position"].size() == 3) { GetEntity()->SetPosition(properties["position"][0], properties["position"][1], properties["position"][2]); } if (properties["rotation"].is_array() && properties["rotation"].size() == 3) { GetEntity()->SetRotation(properties["rotation"][0], properties["rotation"][1], properties["rotation"][2]); } navMesh.reset(); if (!scene->navmeshes.empty()) { navMesh = scene->navmeshes[0]; } return BaseComponent::Load(properties, binstream, scene, flags, extra); } bool Unit::Save(table& properties, shared_ptr<Stream> binstream, shared_ptr<Scene> scene, const SaveFlags flags, shared_ptr<Object> extra) { properties["isFullPlayerControl"] = isFullPlayerControl; properties["isPlayer"] = isPlayer; properties["team"] = team; properties["health"] = health; properties["enabled"] = enabled; if (targetWeak.lock()) { properties["target"] = targetWeak.lock()->GetUuid(); } if (targetPoint) { properties["targetPoint"] = targetPoint->GetUuid(); } properties["isForcedMovement"] = isForcedMovement; auto position = GetEntity()->GetPosition(true); properties["position"] = {}; properties["position"][0] = position.x; properties["position"][1] = position.y; properties["position"][2] = position.z; auto rotation = GetEntity()->GetRotation(true); properties["rotation"] = {}; properties["rotation"][0] = rotation.x; properties["rotation"][1] = rotation.y; properties["rotation"][2] = rotation.z; return BaseComponent::Save(properties, binstream, scene, flags, extra); } void Unit::Damage(const int amount, shared_ptr<Entity> attacker) { if (!isAlive()) { return; } health -= amount; auto world = GetEntity()->GetWorld(); if (!world) { return; } auto now = world->GetTime(); if (health <= 0) { Kill(attacker); } else if (!isInPain && now - painCooldownTime > painCooldown) { isInPain = true; isAttacking = false; auto model = GetEntity()->As<Model>(); if (model) { model->StopAnimation(); model->Animate(painName, 1.0f, 100, ANIMATION_ONCE); } if (agent) { agent->Stop(); } } if (healthBar) { //reducing health bar sprite width healthBar->SetScale((float)health / (float)maxHealth, 1, 1); } //attack an atacker if (!isForcedMovement && !isForcedTarget) { attack(attacker); } } void Unit::Kill(shared_ptr<Entity> attacker) { auto entity = GetEntity(); if (!entity) { return; } auto model = entity->As<Model>(); if (model) { model->StopAnimation(); model->Animate(deathName, 1.0f, 250, ANIMATION_ONCE); } if (agent) { //This method will cancel movement to a destination, if it is active, and the agent will smoothly come to a halt. agent->Stop(); } //to remove nav agent entity->Detach(); agent = nullptr; //to prevent it being obstacle entity->SetCollisionType(COLLISION_NONE); //to prevent selection entity->SetPickMode(PICK_NONE); isAttacking = false; healthBar = nullptr; healthBarBackground = nullptr; if (decayTime > 0) { removeEntityTimer = UltraEngine::CreateTimer(decayTime); ListenEvent(EVENT_TIMERTICK, removeEntityTimer, RemoveEntityCallback, Self()); } } bool Unit::isAlive() { return health > 0 && GetEntity(); } bool Unit::RemoveEntityCallback(const UltraEngine::Event& ev, shared_ptr<UltraEngine::Object> extra) { auto unit = extra->As<Unit>(); unit->removeEntityTimer->Stop(); unit->removeEntityTimer = nullptr; unit->sceneWeak.lock()->RemoveEntity(unit->GetEntity()); return false; } void Unit::scanForTarget() { auto entity = GetEntity(); auto world = entity->GetWorld(); if (world) { //We only want to perform this few times each second, staggering the operation between different entities. //Pick() operation is kinda CPU heavy. It can be noticeable in Debug mode when too much Picks() happes in same game cycle. //Did not notice yet it in Release mode, but it's better to have it optimized Debug as well anyway. auto now = world->GetTime(); if (now < nextScanForTargetTime) { return; } nextScanForTargetTime = now + Random(100, 200); auto entityPosition = entity->GetPosition(true); Vec3 positionLower = entityPosition; positionLower.x = positionLower.x - perceptionRadius; positionLower.z = positionLower.z - perceptionRadius; positionLower.y = positionLower.y - perceptionRadius; Vec3 positionUpper = entityPosition; positionUpper.x = positionUpper.x + perceptionRadius; positionUpper.z = positionUpper.z + perceptionRadius; positionUpper.y = positionUpper.y + perceptionRadius; //will use it to determinate which target is closest float currentTargetDistance = -1; //GetEntitiesInArea takes positions of an opposite corners of a cube as params for (auto const& foundEntity : world->GetEntitiesInArea(positionLower, positionUpper)) { auto foundUnit = foundEntity->GetComponent<Unit>(); //targets are only alive enemy units if (!foundUnit || !foundUnit->isAlive() || !foundUnit->isEnemy(team) || !foundUnit->GetEntity()) { continue; } float dist = foundEntity->GetDistance(entity); if (dist > perceptionRadius) { continue; } //check if no obstacles like walls between units auto pick = world->Pick(entity->GetBounds(BOUNDS_RECURSIVE).center, foundEntity->GetBounds(BOUNDS_RECURSIVE).center, perceptionRadius, true, RayFilter, Self()); if (dist < 0 || currentTargetDistance < dist) { targetWeak = foundEntity; currentTargetDistance = dist; } } } } void Unit::Update() { if (!GetEnabled() || !isAlive()) { return; } auto entity = GetEntity(); auto world = entity->GetWorld(); auto model = entity->As<Model>(); if (!world || !model) { return; } if (isFullPlayerControl) { return; } //making health bar fllow the unit auto window = ActiveWindow(); if (window && healthBar && healthBarBackground) { auto framebuffer = window->GetFramebuffer(); auto position = entity->GetBounds().center; position.y += entity->GetBounds().size.height / 2;//take top position of unit shared_ptr<Camera> camera = cameraWeak.lock(); if (camera) { //transorming 3D position into 2D auto unitUiPosition = camera->Project(position, framebuffer); //sprite Y coordinate start from bottom of screen and projected from top unitUiPosition.y = framebuffer->size.height - unitUiPosition.y; unitUiPosition.x -= healthBarBackground->size.width / 2; healthBar->SetPosition(unitUiPosition.x, unitUiPosition.y); healthBarBackground->SetPosition(unitUiPosition.x, unitUiPosition.y); bool doShow = isSelected || (health != maxHealth && !isPlayer); healthBar->SetHidden(!doShow); healthBarBackground->SetHidden(!doShow); } } //can't attack or move while pain animation if (isInPain) { return; } bool isMoving = false; //ignore enemies and move if (isForcedMovement && goTo()) { return; } //atacking part if (!isMoving) { auto target = targetWeak.lock(); // Stop attacking if target is dead if (target) { float distanceToTarget = entity->GetDistance(target); bool doResetTarget = false; if (distanceToTarget > chaseMaxDistance && !isForcedTarget) { doResetTarget = true; } else { for (auto const& targetComponent : target->components) { auto targetUnit = targetComponent->As<Unit>(); if (targetUnit && !targetUnit->isAlive()) { doResetTarget = true; isForcedTarget = false; } break; } } if (doResetTarget) { target.reset(); targetWeak.reset(); if (agent) { agent->Stop(); } } } if (isAttacking && target != nullptr) { //rotating unit to target float a = ATan(entity->matrix.t.x - target->matrix.t.x, entity->matrix.t.z - target->matrix.t.z); if (agent) { agent->SetRotation(a + 180); } } if (!target) { scanForTarget(); } if (target) { float distanceToTarget = entity->GetDistance(target); //run to target if out of range if (distanceToTarget > attackRange) { if (agent) { agent->Navigate(target->GetPosition(true)); } model->Animate(runName, 1.0f, 250, ANIMATION_LOOP); } else { if (agent) { agent->Stop(); } //start attack if did not yet if (!isAttacking) { meleeAttackTime = world->GetTime(); model->Animate(attackName, 1.0f, 100, ANIMATION_ONCE); isAttacking = true; } } return; } } if (targetPoint && goTo()) { return; } if (!isAttacking) { model->Animate(idleName, 1.0f, 250, ANIMATION_LOOP); if (agent) { agent->Stop(); } } } bool Unit::RayFilter(shared_ptr<Entity> entity, shared_ptr<Object> extra) { shared_ptr<Unit> thisUnit = extra->As<Unit>(); shared_ptr<Unit> pickedUnit = entity->GetComponent<Unit>(); //skip if it's same team return pickedUnit == nullptr || pickedUnit && pickedUnit->team != thisUnit->team; } void Unit::AttackHook(shared_ptr<Skeleton> skeleton, shared_ptr<Object> extra) { auto unit = std::dynamic_pointer_cast<Unit>(extra); if (!unit) { return; } auto entity = unit->GetEntity(); auto target = unit->targetWeak.lock(); if (target) { auto pos = entity->GetPosition(true); auto dest = target->GetPosition(true) + target->GetVelocity(true); //attack in target in range if (pos.DistanceToPoint(dest) < unit->attackRange) { for (auto const& targetComponent : target->components) { auto base = targetComponent->As<BaseComponent>(); if (base) { base->Damage(unit->attackDamage, entity); } } } } } void Unit::EndAttackHook(shared_ptr<Skeleton> skeleton, shared_ptr<Object> extra) { auto unit = std::dynamic_pointer_cast<Unit>(extra); if (unit) { unit->isAttacking = false; } } void Unit::EndPainHook(shared_ptr<Skeleton> skeleton, shared_ptr<Object> extra) { auto unit = extra->As<Unit>(); if (unit) { unit->isInPain = false; if (unit->isAlive() && unit->GetEntity()->GetWorld()) { unit->painCooldownTime = unit->GetEntity()->GetWorld()->GetTime(); } } } bool Unit::isEnemy(int otherUnitTeam) const { return team == 1 && otherUnitTeam == 2 || team == 2 && otherUnitTeam == 1; } void Unit::goTo(Vec3 positionToGo, bool isForced) { auto entity = GetEntity(); if (entity) { isForcedMovement = isForced; targetPoint = CreatePivot(entity->GetWorld()); targetPoint->SetPosition(positionToGo); goTo(); } } bool Unit::goTo() { bool doMove = false; auto entity = GetEntity(); auto model = entity->As<Model>(); if (targetPoint && agent) { doMove = agent->Navigate(targetPoint->GetPosition(true), 100, 2.0f); if (doMove) { //checking distance to target point on nav mesh float distanceToTarget = entity->GetDistance(agent->GetDestination()); if (distanceToTarget < targetPointDistance) { auto wayPoint = targetPoint->GetComponent<WayPoint>(); if (wayPoint && wayPoint->getNextPoint()) { targetPoint = wayPoint->getNextPoint(); doMove = true; } else { targetPoint.reset(); doMove = false; } } else { doMove = true; } } if (doMove && model) { model->Animate(runName, 1.0f, 250, ANIMATION_LOOP); } } return doMove; } void Unit::attack(shared_ptr<Entity> entityToAttack, bool isForced) { targetWeak.reset(); if (!entityToAttack || !entityToAttack->GetComponent<Unit>() || entityToAttack->GetComponent<Unit>()->team == team) { return; } targetPoint.reset(); isForcedMovement = false; isForcedTarget = isForced; targetWeak = entityToAttack; } void Unit::select(bool doSelect) { isSelected = doSelect; } For Unit class you need the WayPoint component from the previous tutorial. Also can be download here: Remember adding new component to ComponentSystem.h Unit files:Unit.zip Strategy Controller component Strategy Controller will be used to control player units: Selecting a unit by left click, doing it with Control will add new unit to already selected Clicking on something else reset unit selection Holding left mouse button will create selection box that will select units in it once button released Right click to make units go somewhere ignoring enemies or attacking specific enemy Its path will be: "Source\Components\Player" StrategyController.json: { "component": { "properties": [ { "name": "playerTeam", "label": "Player Team", "value": 1 } ] } } StrategyController.h #pragma once #include "UltraEngine.h" using namespace UltraEngine; class StrategyController : public Component { protected: vector<std::weak_ptr<Entity>> selectedUnits; //Control key state bool isControlDown = false; int playerTeam = 1; std::weak_ptr<Camera> cameraWeak; shared_ptr<Sprite> unitSelectionBox; //first mouse position when Mouse Left was pressed iVec2 unitSelectionBoxPoint1; //height of selection box float selectHeight = 4; //mouse left button state bool isMouseLeftDown = false; //draw or hide selection box void updateUnitSelectionBox(); bool selectUnitsByBox(shared_ptr<Camera> camera, shared_ptr<Framebuffer> framebuffer, iVec2 unitSelectionBoxPoint2); void deselectAllUnits(); static bool RayFilter(shared_ptr<Entity> entity, shared_ptr<Object> extra); public: StrategyController(); ~StrategyController() override; shared_ptr<Component> Copy() override; bool Load(table& properties, shared_ptr<Stream> binstream, shared_ptr<Map> scene, const LoadFlags flags, shared_ptr<Object> extra) override; bool Save(table& properties, shared_ptr<Stream> binstream, shared_ptr<Map> scene, const SaveFlags flags, shared_ptr<Object> extra) override; void Update() override; void Start() override; bool ProcessEvent(const Event& e) override; }; StrategyController.cpp #include "UltraEngine.h" #include "StrategyController.h" #include "../AI/Unit.h" StrategyController::StrategyController() { name = "StrategyController"; } shared_ptr<Component> StrategyController::Copy() { return std::make_shared<StrategyController>(*this); } StrategyController::~StrategyController() = default; void StrategyController::Start() { auto entity = GetEntity(); entity->AddTag("StrategyController"); //Listen() needed for calling ProcessEvent() in component when event happen Listen(EVENT_MOUSEDOWN, nullptr); Listen(EVENT_MOUSEUP, nullptr); Listen(EVENT_MOUSEMOVE, nullptr); Listen(EVENT_KEYUP, nullptr); Listen(EVENT_KEYDOWN, nullptr); //optimal would be setting component to a camera if (entity->As<Camera>()) { cameraWeak = entity->As<Camera>(); } else { //otherwise let's get it by tag for (auto const& cameraEntity : GetEntity()->GetWorld()->GetTaggedEntities("Camera")) { cameraWeak = cameraEntity->As<Camera>(); break; } } // 1/1 size for pixel accuarcy scaling unitSelectionBox = CreateSprite(entity->GetWorld(), 1, 1); //transparent green color unitSelectionBox->SetColor(0, 0.4f, 0.2, 0.5f); unitSelectionBox->SetPosition(0, 0, 0.00001f); unitSelectionBox->SetRenderLayers(2); unitSelectionBox->SetHidden(true); //to make sprite transparent auto material = CreateMaterial(); material->SetShadow(false); material->SetTransparent(true); material->SetPickMode(false); //Unlit removes any effect that would light draw on material material->SetShaderFamily(LoadShaderFamily("Shaders/Unlit.fam")); unitSelectionBox->SetMaterial(material); } bool StrategyController::Load(table& properties, shared_ptr<Stream> binstream, shared_ptr<Map> scene, const LoadFlags flags, shared_ptr<Object> extra) { Component::Load(properties, binstream, scene, flags, extra); if (properties["playerTeam"].is_number()) playerTeam = properties["playerTeam"]; return true; } bool StrategyController::Save(table& properties, shared_ptr<Stream> binstream, shared_ptr<Map> scene, const SaveFlags flags, shared_ptr<Object> extra) { Component::Save(properties, binstream, scene, flags, extra); properties["playerTeam"] = playerTeam; return true; } void StrategyController::Update() { updateUnitSelectionBox(); } bool StrategyController::ProcessEvent(const Event& e) { auto window = ActiveWindow(); if (!window) { return true; } auto mousePosition = window->GetMousePosition(); auto camera = cameraWeak.lock(); switch (e.id) { case EVENT_MOUSEDOWN: if (!camera) { break; } if (e.data == MOUSE_LEFT) { unitSelectionBoxPoint1 = iVec2(mousePosition.x, mousePosition.y); isMouseLeftDown = true; //move or attack on Right Click } else if (e.data == MOUSE_RIGHT) { //getting entity under cursor auto pick = camera->Pick(window->GetFramebuffer(), mousePosition.x, mousePosition.y, 0, true); if (pick.success && pick.entity) { auto unit = pick.entity->GetComponent<Unit>(); if (unit && unit->isAlive() && unit->team != playerTeam) { for (auto const& entityWeak : selectedUnits) { auto entityUnit = entityWeak.lock(); if (entityUnit && entityUnit->GetComponent<Unit>()) { entityUnit->GetComponent<Unit>()->attack(pick.entity, true); } } } else { for (auto const& entityWeak : selectedUnits) { auto entityUnit = entityWeak.lock(); if (entityUnit && entityUnit->GetComponent<Unit>()) { entityUnit->GetComponent<Unit>()->goTo(pick.position, true); } } } } } break; case EVENT_MOUSEUP: if (!camera) { break; } //unit selection on Left Click if (e.data == MOUSE_LEFT) { if (!selectUnitsByBox(camera, window->GetFramebuffer(), iVec2(mousePosition.x, mousePosition.y))) { auto pick = camera->Pick(window->GetFramebuffer(), mousePosition.x, mousePosition.y, 0, true); if (pick.success && pick.entity) { auto unit = pick.entity->GetComponent<Unit>(); if (unit && unit->isPlayer && unit->isAlive()) { if (!isControlDown) { deselectAllUnits(); } selectedUnits.push_back(pick.entity); unit->select(); } else { deselectAllUnits(); } } else { deselectAllUnits(); } } isMouseLeftDown = false; } break; case EVENT_MOUSEMOVE: break; case EVENT_KEYUP: if (e.data == KEY_CONTROL) { isControlDown = false; } break; case EVENT_KEYDOWN: if (e.data == KEY_CONTROL) { isControlDown = true; } break; } return true; } void StrategyController::deselectAllUnits() { for (auto const& entityWeak : selectedUnits) { auto entityUnit = entityWeak.lock(); if (entityUnit && entityUnit->GetComponent<Unit>()) { entityUnit->GetComponent<Unit>()->select(false); } } selectedUnits.clear(); } void StrategyController::updateUnitSelectionBox() { if (!isMouseLeftDown) { unitSelectionBox->SetHidden(true); } else { auto window = ActiveWindow(); if (window) { auto mousePosition = window->GetMousePosition(); iVec2 unitSelectionBoxPoint2(mousePosition.x, mousePosition.y); iVec2 upLeft(Min(unitSelectionBoxPoint1.x, unitSelectionBoxPoint2.x), Min(unitSelectionBoxPoint1.y, unitSelectionBoxPoint2.y)); iVec2 downRight(Max(unitSelectionBoxPoint1.x, unitSelectionBoxPoint2.x), Max(unitSelectionBoxPoint1.y, unitSelectionBoxPoint2.y)); //don't show Selection Box if it's only few pixels and could be single click to select unit if ((downRight.x - upLeft.x < 4) || (downRight.y - upLeft.y < 4)) { unitSelectionBox->SetHidden(true); return; } unitSelectionBox->SetPosition(upLeft.x, window->GetFramebuffer()->GetSize().height - downRight.y); auto width = downRight.x - upLeft.x; auto height = downRight.y - upLeft.y; //changing sprite size via scale, just size is readonly unitSelectionBox->SetScale(width, height, 1); unitSelectionBox->SetHidden(false); } } } bool StrategyController::selectUnitsByBox(shared_ptr<Camera> camera, shared_ptr<Framebuffer> framebuffer, iVec2 unitSelectionBoxPoint2) { if (!unitSelectionBox || unitSelectionBox->GetHidden() || !camera || !framebuffer) { return false; } iVec2 upLeft(Min(unitSelectionBoxPoint1.x, unitSelectionBoxPoint2.x), Min(unitSelectionBoxPoint1.y, unitSelectionBoxPoint2.y)); iVec2 downRight(Max(unitSelectionBoxPoint1.x, unitSelectionBoxPoint2.x), Max(unitSelectionBoxPoint1.y, unitSelectionBoxPoint2.y)); auto pick1 = camera->Pick(framebuffer, upLeft.x, upLeft.y, 0, true, RayFilter); auto pick2 = camera->Pick(framebuffer, downRight.x, downRight.y, 0, true, RayFilter); if (!pick1.success || !pick2.success) { return false; } deselectAllUnits(); //first param GetEntitiesInArea should has lower coordinates than second Vec3 positionLower = Vec3(Min(pick1.position.x, pick2.position.x), Min(pick1.position.y, pick2.position.y), Min(pick1.position.z, pick2.position.z)); Vec3 positionUpper = Vec3(Max(pick1.position.x, pick2.position.x), Max(pick1.position.y, pick2.position.y), Max(pick1.position.z, pick2.position.z)); positionUpper.y = positionUpper.y + selectHeight; for (auto const& foundEntity : camera->GetWorld()->GetEntitiesInArea(positionLower, positionUpper)) { auto foundUnit = foundEntity->GetComponent<Unit>(); //targets are only alive enemy units if (!foundUnit || !foundUnit->isAlive() || !foundUnit->isPlayer || foundUnit->team != playerTeam) { continue; } selectedUnits.push_back(foundUnit->GetEntity()); foundUnit->select(); } return true; } bool StrategyController::RayFilter(shared_ptr<Entity> entity, shared_ptr<Object> extra) { shared_ptr<Unit> pickedUnit = entity->GetComponent<Unit>(); //skip if it's unit return pickedUnit == nullptr; } StrategyController.zip Also we need top down camera component, you can find it here, if you don't have it yet: Prefabs Create Prefabs folder in project root folder. Open the Editor, add camera to empty scene, call it StrategyCamera and add to it TopDownCamera and StrategyController components. You might also want to change a FOV in Camera tab in entity properties. To make a prefab do right click on camera in Scene tab and press "Save as Prefab": Once you want to change something without having to do it on every map, just open the prefab .pfb file and do a change there. Now create a Units subfolder in the Prefabs folder for two units. Add to scene Paladin model. Add Paladin component, click on "Is Player Unit" checkbox and change Attack Frame to 40 as it's a frame when sword will hit a target: Save as Prefab in Pefabs/Units as Paladin.pfb You can remove now Paladin from scene and add Warrok. In its Unit component team will be Bad, health and max health 120, speed 2.0, attack range 1.5, attack damage 25, and Attack Frame 22 as it's attack faster. Simple Strategy map creation Create big flat brush as a ground Add Navigation map - change Agent Radius to 0.5 and Agent Height 2.0 so Warrok could fit it. Tile size be default is 8 m² = Voxel size (0.25) * Tile Resolution (32). It's better not to touch those until you know what you are doing. To change Nav Mesh size just change Tile count in first line to make it fit created ground. For 40 m² it will be 5x5 tiles. Drag a ground material from Project tab "\Materials\Ground\Ground036.mat" to the ground. If it's blurry, select brush, choose Edit Face mode and increase a scale there. Select translate mode and drag'n'drop to scene prefabs StrategyCamera, Warrok and Paladin. You can copy entities with Shift + dragging. To edit a prefab entity you will need to "break" prefab. To do it click on the Lock icon in properties and Ok in the dialog. Also you need to break prefab to make component get real Scene, because otherwise in it will be prefab scene in Component's Load() Simple map is ready. You can make it a little bit complicated by adding couple cycled WayPoints for Warroks. Add WayPoint to Target Point. strategy.zip In Game.cpp update GameMenuButtonCallback function to avoid cursor being hidden: bool Game::GameMenuButtonCallback(const Event& ev, shared_ptr<Object> extra) { if (KEY_ESCAPE == ev.data && extra) { auto game = extra->As<Game>(); bool isHidden = game->menuPanel->GetHidden(); game->menuPanel->SetHidden(!isHidden); if (game->player) { //we can get a game window anywhere, but take in mind that it will return nullptr, if window is not active ;) auto window = ActiveWindow(); //checking just in case if we actually got a window if (window) { //hiding cursor when hiding a menu and vice versa window->SetCursor(isHidden ? CURSOR_DEFAULT : CURSOR_NONE); } game->player->doResetMousePosition = !isHidden; } //If the callback function returns false no more callbacks will be executed and no event will be added to the event queue. //to avoid double call return false; } return true; } Game and Menu.zip In result should be something like that: Final version here: https://github.com/Dreikblack/CppTutorialProject/tree/3-making-strategy-game
  2. I was wondering if anyone is able to help me. So I have had an issue for months now with Enemies crashing my game. Regardless of the models/animations I use. Regardless of the scripts or code I use (I have tried the Default, The HUD elements ai script and about 4 variations I have made myself). Simply having enemies in my project causes crashes. I have tried running my game without enemies and it runs perfectly. But even if I set them to run away instead of attacking, simply having enemy characters causes the game to crash. I would really love and appreciate some help fixing this because its an issue that makes my game unplayable and the only major bug I have left to fix.
  3. Hello dear reader, First of all, as I recently though on how to make the blog more interesting and valuable for you to read, I thought it would be a cool idea if I provide a gamedev specifc tip (can be everything from productivity, to technical stuff, to marketing) at the end of every blog entry. I would be really happy, if you tell me what you think of this idea. Back to topic. I want to present what I am working at the moment. Last time it were some animations and rigs. I had some more trouble with them then expected and needed to do all that stuff again (rigging and animations). As they are just meant for a visual impact for me to be able to build and optimise the systems (e.g combat, movement) they don't look very professional, but my intention was not to create high quality game ready animations . Enough about the past, I will showcase the animations any time soon, as we take a look on the combat system maybe. So what I am doing now? Well I made some good progress and have some quite cool stuff to show. I was heavily working on my npc/character system (called "Character Set"). The NPC is now able to wear diffrent cloths. Already provided are: Helmet, Body, Gloves, Legs, Boots. At the moment I am working on the weapon system. So you can easily equip, range and melee weapons to the character. As I slowly would like to get a little bit more into detail here the settings for the character set: As you see I try to keep everything as clean and userfriendly as possible. A very cool thing are the "NPC Settings" which gives the systems a lot of power as it keeps the script settings clean, and gives you a lot of options to costumize the way your NPC behaves. It even gives you access to some technical stuff you can change, so there are no boundaries for you when using diffrent rigs/animation systems etc. Maybe I will talk more detailed about the whole AI/Character Set stuff another time. Another cool progress I made is the "PHFW Animation Manager". As my system works not with the Animation name (e.g "Walking", "Running"), but with the animation index, this tool is very helpful. Why I use the animation index you may ask. Well I use my own animation playing system (not the Leadwerks "PlayAnimation" function) and unfortunately the "SetAnimationFrame" function of Leadwerks does not provide to use animation names. To work around this I created this tool. This is the first external tool for the phodex framework and there are some more to come, I will then, most likley, put them into a huge toolset. There you can also set various keyframes needed for the combat system, so the tool can still be helpful, if the "SetAnimationFrame" function gets enhanced. This is how it currently looks (its like a pre alpha :D, just set up the rough system) So as promised here is the tip: Game Dev Tip #1: If you have a problem, you struggle with for a long time and you just can't find a solution. Stop working and do something completely diffrent (for example go outside and take a walk), then come back with a fresh mind and you will be suprised what great effect this can have, how you find new ideas, or simply get new motivation. I hope that helps some of you. I try to provide some more visual content to show in the future as I know its much more fun to watch pictures & videos instead of reading long and boring texts . Just the best and stay cool . Markus from Phodex
  4. Hey, I am currently working on my AI, and I wonder what would be a good way, to make my NPC hold a specific distance to the player. In my case I need it for my AI behavior when it is using range weapons. I think a nice idea is to let the AI find a random point, which keeps a few conditions like, 10 units away from player, not outside of the map, not inside another object. But how to realise that. I would be very thankful if you can give me ideas, examples, descriptions etc.
  5. Hi, Just enjoying this new release of Leadwerks 4.4 and decided to make a map with vegetation just for fun. Not really tried the terrain tool before today, and I'm really impressed. It really good and precise to use! I'm using the Zombie pack addon and the fps pack addon. I've placed 5 zombies in a group near of a "bunker" i've made with brushes. I've got the game almost stop when the zombie see the player, and found that the game update were going in 7000+ms update time, was not a render problem. In debug mode, the FPS drop to about 0-1 fps. ... EDIT: Fixed the issue for the update time, by creating a new project and a new map. Adding back the zombies did not create any issues. So I suspect that is related to all the changes since then. The "mygame" map I was using, was made since Leadwerks 3.1 and was updated by the project manager at each release! EDIT2: Found out the REAL issue with it. I started putting tons of vegetation again and enabled COLLISIONS for the tree and this is what make the problem with the NPC. Disabling the collision on the vegetation layer fixed it. So something make the update really-really long if you put collisions on vegetation models and use NPC's.... With the new map, I was not able to have the game work in debug because it crashed on launch. (I've recompiled the game with MSVC), but the release build work 100% ("esc" key now display me the new GUI menu) The debug build seem to crash on newton... Is this because I've recompiled the project with MSVC community 2015? I've got a game crash each time the player is being killed by NPC. I tested it with the crawler and the zombies and it's doing the same thing. Each time the player is being killed, the game crash with an exception error. (The screenshot is the last to display when the player is killed and the game crash)
  6. Hi, I would like to report a problem that I've seen with the NPC that are making the "update" loop much longer when they "awake". Description of the problem: On a terrain that have trees(vegetation layer) with collision enabled, when the player (std FPS controller) get near a NPC(crawler) the update loop can get as high as 7000ms for a few second and come back as normal. Disabling the collision option on the vegetation layer, seemed to fix it. But I "feel" that it does that for every type of collisions (models / brushes). The more you put on the map, the more time it will take on the update loop. Reproductability: 100% How to reproduce: 1. Create map with the FPS template. 2. Create a terrain. 3. Place some (3+) Crawlers NPC on the terrain. 4. Create lots of tree (density of 2.2 or less), and enable the collision option on the layer. 5. Start the game and move the FPS player near one of the NPC. 6. Notice the "lag" and stop when the NPC emit the detect sound. Also notice the update loop time increasing a lot! My impressions about this: I feel like the navigation system is doing some kind of data copy of collision models from all the surrounding with no filtering of geometry when the AI find the play "in range". An investigation would be welcome... My current system that was used for the testing: CPU Intel I7 3770K, 16Gb ram, 512GB SSD + 1TBHDD, NVIDIA GTX1080 at 3840x2160 desktop resolution. Windows 10 Home (version 1703)
  7. Hi everyone!I have an unusual problem.Every creature with MosterAI attacks with it`s back.Looks very strange)How can i fix it?
  8. Hi, any idea haw can i change mode Chase player (Monster ai) to run away from player. thank y
  9. I have some simple flying AI enemies that close distance to a player and I've started out just using simple distance checks, turns and moves as I thought that using standard character controller monster AI would not work for flying enemies. Is it right that character controller is not suitable for flying enemies,or should it work if set up or 'faked' right (e.g. some pivot to act as a 'ground foot' or something - fro me it may not be suitable as my enemies descend from high and should pass over obstacles they are currently above)? I'm thinking the character controller is designed for enemies that touch the ground in order to make use of the navigation mesh? Anyone got any feedback on how they went about flying enemies?
  10. Hello, currently I am working on my AI system, but I have a problem setting up multiple NPCs. What you need to know: - My AI is able to randomly move around in the world using waypoints. - I coded an own animation manager "class" The Problem: I set one NPC to not move around and the other to do so. It works fine. One is moving, one is standing still, BUT the one moving is not playing an animation. So he is kind of floating around. The one who is standing is playing the walk animation. So I printed some values to find the error. By the way if I just have on NPC active everything works fine without any trouble. This is the code of my animation manager class: function AnimationManager(script) local model = {} function model:StartAnimation(animation, speed, objects, playOnce, reset, blendMultiplier) --some code self.objects = objects local printIt = self.objects[1]:GetKeyValue("name") System:Print(printIt) end function model:DrawAnimation() --some code end return model end The scripts are called like this: function Script:Start() --Load Classes self.loadClass = AnimationManager self.animManager = self:loadClass() --Start Animation self.animManager:StartAnimation(argument1, argument2, ...) end function Script:Draw() --To play an animation self.animManager:DrawAnimation() end I only put the nessecary information in the code block, that you understand what I am talking about. My NPCs have diffrent names in the editor. The moving one is named "Fox" the standing one "Jacob". The class works, that is not the problem. In the AI code I printed the name of the NPC who is calling the StartAnimation() function. It prints "Fox", so the correct NPC is calling StartAnimation(). The NPC called "Jacob" is NOT calling StartAnimation(). The NPC is sending his entity (self.entity) so the animation manager knows on which model to perform the animation. In the animation manager if I print the name of the entity to play the Animation on it prints "Jacob". o.O Why does this happen how is it possible that the two NPC scripts seems to communicate with each other. Why does the AI script of "Jacob" send its information? I am really, really confused. I am pretty sure I am doing something wrong. I watched this behavior before, but now it was the time to ask you guys for help, because I dont find the error. P.S.: In function Script:Start() of the AI script I set self.npcModel[1] = self.entity to be able to play the animation on multiple models at once. self.npcModel is then sent to the animation manager.
  11. Quick demo video of my Utility and Needs Based UI test framework. More detail on the link.
  12. Hi, I'm using the beta version on steam, using it on Window 10 Home (French) currently. My graphic card is a NVidia GTX 780 I've buyed yesterday the MERC model and tryied it in a quick level made with brushes. I really like it and it's so simple to use it! But I've seen some issues on how leadwerks seem to make NPC "see" in their environment. On the level made of brushes, I had to enter the room where the merc was to see me and then he started to chase. I've exported the map in .OBJ (Great feature!) and imported it in MAX then done a quick bake of lighting and imported it back, mostly to test if the scale/position was not changed in the export/import. Everything worked, I was able to make the collision shape from the tools provided in Leadwerks. Collision is working perfectly, and the map work as before except this: If the brushes are hidden or removed, the NPC will see me immediately and start chasing me (using the NAVMESH to get to me even if I'm behind a door / wall). For Leadwerks the level that has that problem has no brushes and only is composed of static meshes. So if I want to fix this temporary, I will have to create brushes and hide them inside the model, so the NPC`s will not be able to look thru the wall. Doing this will surely decrease the performance.
  13. When I use this function all happens is that the player glitches out. I see that it has been avoided with the DirectMoveToTarget function inside the MosterAi script, which has no pathfinding but just walking straight towards the target. It would still be better to have a path while following a target that may be behind an obstacle, when the function is called. GoToPoint works fine on (for example) pivots in the scene, even when its position is set somewhere else really fast (and the funcion is called once after every position change).
  14. Hey everyone. I'm working on a level and after watching the AI tutorial multiple times, I still cant figure out how to get the AI to walk to the waypoint. Any help would be greatly appreciated.
  15. Hello, im having trouble releasing dead enemy after some time. When i kill enemy i want body to disappear after 5 sec. I have change AI script , have no errors but it does not work. Can someone help ? Here is my code. Thanks import "Scripts/AnimationManager.lua" import "Scripts/Functions/GetEntityNeighbors.lua" --Public values Script.health=40--int "Health" Script.enabled=true--bool "Enabled" Script.target=nil--entity "Target" Script.sightradius=3--float "Sight Range" Script.senseradius=3--float "Hearing Range" Script.teamid=2--choice "Team" "Neutral,Good,Bad" --Private values Script.damage=5 Script.attackrange=1 Script.updatefrequency=500 Script.lastupdatetime=0 Script.prevtarget=nil Script.animation={} Script.animation.idle=0 Script.animation.run=1 Script.animation.attack={} Script.animation.attack[0]=2 Script.animation.attack[1]=2 Script.animation.death=3 Script.followingtarget=false Script.maxaccel=10 Script.speed=1.5 Script.lastupdatetargettime=0 Script.attackmode=0 Script.attackbegan=0 Script.sound={} Script.sound.death = Sound:Load("Sound/Characters/zombiecrawlerdie.wav") Script.sound.alert = Sound:Load("Sound/Characters/zombiecrawlerintro.wav") Script.sound.attack={} Script.sound.attack[1] = Sound:Load("Sound/Characters/zombiecrawlerswipe.wav") Script.sound.attack[2] = Sound:Load("Sound/Characters/zombiecrawlerswipe.wav") Script.removeBodyTimer = 0 Script.removeBodyTime = 5 function Script:Enable()--in if self.enabled==false then if self.health>0 then self.enabled=true if self.target~=nil then self:SetMode("roam") else self:SetMode("idle") end end end end function Script:ChooseTarget() local entities = GetEntityNeighbors(self.entity,self.sightradius,true) local k,entity for k,entity in pairs(entities) do if entity.script.teamid~=nil and entity.script.teamid~=0 and entity.script.teamid~=self.teamid then if entity.script.health>0 then local d = self.entity:GetDistance(entity) --if d<self.senseradius then -- return entity.script --else -- local p = Transform:Point(entity:GetPosition(),nil,self.entity) -- if (p.z>-math.abs(p.x) and p.z<-math.abs(p.y)) then local pickinfo=PickInfo() if self.entity.world:Pick(self.entity:GetPosition()+Vec3(0,1.6,0),entity:GetPosition()+Vec3(0,1.6,0),pickinfo,0,false,Collision.LineOfSight)==false then return entity.script end -- end --end end end end end function Script:DistanceToTarget() local pos = self.entity:GetPosition() local targetpos = self.target.entity:GetPosition() if math.abs(targetpos.y-pos.y)<1.5 then return pos:xz():DistanceToPoint(targetpos:xz()) else return 100000--if they are on different vertical levels, assume they can't be reached end end function Script:TargetInRange() local pos = self.entity:GetPosition() local targetpos = self.target.entity:GetPosition() if math.abs(targetpos.y-pos.y)<1.5 then if pos:xz():DistanceToPoint(targetpos:xz())<self.attackrange then return true end end return false end --[[ function WorldGetEntitiesInAABBDoCallback(entity,extra) if entity~=extra then if GetEntityNeighborsScriptedOnly==false or entity.script~=nil then table.insert(WorldGetEntitiesInAABBDoCallbackTable,entity) end end end function GetEntityNeighbors(entity,radius,scriptOnly) local result local aabb = AABB() local p = entity:GetPosition(true) local temp = GetEntityNeighborsScriptedOnly GetEntityNeighborsScriptedOnly=scriptOnly aabb.min = p - radius aabb.max = p + radius aabb:Update() local table = WorldGetEntitiesInAABBDoCallbackTable WorldGetEntitiesInAABBDoCallbackTable = {} entity.world:ForEachEntityInAABBDo(aabb,"WorldGetEntitiesInAABBDoCallback",entity) result = WorldGetEntitiesInAABBDoCallbackTable WorldGetEntitiesInAABBDoCallbackTable = table GetEntityNeighborsScriptedOnly = temp return result end ]]-- function Script:Start() self.animationmanager = AnimationManager:Create(self.entity) if self.enabled then if self.target~=nil then self:SetMode("roam") else self:SetMode("idle") end end end function Script:Hurt(damage,distributorOfPain) if self.health>0 then if self.target==nil then self.target=distributorOfPain self:SetMode("attack") end self.health = self.health - damage if self.health<=0 then self.entity:SetMass(0) self.entity:SetCollisionType(0) self.entity:SetPhysicsMode(Entity.RigidBodyPhysics) self:SetMode("dying") end end end function Script:EndDeath() self:SetMode("dead") self.removeBodyTimer = self.removeBodyTimer + (Time:GetSpeed()/100) end function Script:DirectMoveToTarget() self.entity:Stop() local targetpos = self.target.entity:GetPosition() local pos = self.entity:GetPosition() local dir = Vec2(targetpos.z-pos.z,targetpos.x-pos.x):Normalize() local angle = -Math:ATan2(dir.y,-dir.x) self.entity:SetInput(angle,self.speed) end function Script:SetMode(mode) if mode~=self.mode then local prevmode=self.mode self.mode=mode if mode=="idle" then self.target=nil self.animationmanager:SetAnimationSequence(0,0.02) self.entity:Stop()--stop following anything elseif mode=="roam" then if self.target~=nil then self.animationmanager:SetAnimationSequence(1,0.04) self.entity:GoToPoint(self.target:GetPosition(true),5,5) else self:SetMode("idle") end elseif mode=="attack" then self:EndAttack() elseif mode=="chase" then if self.entity:Follow(self.target.entity,self.speed,self.maxaccel) then if prevmode~="chase" then self.entity:EmitSound(self.sound.alert) end self.followingtarget=true self.animationmanager:SetAnimationSequence(self.animation.run,0.05,300) if self:DistanceToTarget()<self.attackrange*2 then self.followingtarget=false self.entity:Stop() self:DirectMoveToTarget() end else self.target=nil self:SetMode("idle") return end elseif mode=="dying" then self.entity:EmitSound(self.sound.death) self.entity:Stop() self.animationmanager:SetAnimationSequence(self.animation.death,0.04,300,1,self,self.EndDeath) elseif mode=="dead" then self.entity:SetCollisionType(0) self.entity:SetMass(0) self.entity:SetShape(nil) self.entity:SetPhysicsMode(Entity.RigidBodyPhysics) self.enabled=false if (self.removeBodyTimer > self.removeBodyTime) then self.entity:Release() end end end end function Script:EndAttack() if self.mode=="attack" then if self.target.health<=0 then self:SetMode("idle") return end local d = self:DistanceToTarget() if d>self.attackrange then --if d>self.attackrange*2 then self:SetMode("chase") return --else -- local pos = self.entity:GetPosition() -- local targetpos = self.target.entity:GetPosition() -- self.entity:SetInput(-Math:ATan2(targetpos.x-pos.x,targetpos.y-pos.y),self.speed) -- return --end end self.entity:Stop() self.attackmode = 1-self.attackmode--switch between right and left attack modes self.animationmanager:SetAnimationSequence(self.animation.attack[self.attackmode],0.04*math.random(1,1.25),300,1,self,self.EndAttack) self.attackbegan = Time:GetCurrent() if math.random()>0.75 then self.entity:EmitSound(self.sound.attack[self.attackmode+1]) end end end function Script:UpdatePhysics() if self.enabled==false then return end local t = Time:GetCurrent() self.entity:SetInput(self.entity:GetRotation().y,0) if self.mode=="idle" then if t-self.lastupdatetargettime>250 then self.lastupdatetargettime=t self.target = self:ChooseTarget() if self.target then self:SetMode("chase") end end elseif self.mode=="roam" then if self.entity:GetDistance(self.target)<1 then self:SetMode("idle") end elseif self.mode=="chase" then if self.target.health<=0 then self:SetMode("idle") return end if self:TargetInRange() then self:SetMode("attack") elseif self:DistanceToTarget()<self.attackrange*2 then self.followingtarget=false self.entity:Stop() self:DirectMoveToTarget() else if self.followingtarget==false then if self.entity:Follow(self.target.entity,self.speed,self.maxaccel) then self:SetMode("idle") end end end elseif self.mode=="attack" then if self.attackbegan~=nil then if t-self.attackbegan>300 then self.attackbegan=nil self.target:Hurt(self.damage) end end local pos = self.entity:GetPosition() local targetpos = self.target.entity:GetPosition() local dx=targetpos.x-pos.x local dz=targetpos.z-pos.z self.entity:AlignToVector(-dx,0,-dz) end end function Script:Draw() if self.enabled==false then return end self.animationmanager:Update() end
  16. Hello. Im Nikola. Im having a strange problem with AI script. I have few 3d characters , my IDLE animation in not 0 anim, it is 3 or 7. When i change that in script, character still performs zero anim as IDLE. How to fix it? Thanks.
  17. Hello all. As I am experimenting with Leadwerks, I have come across a strange issue. My monster runs on the spot, and wont chase the player. I am using all the default scripts and assets. Heres what I have done: -Drag the player prefab into the 3D viewport -Drag the monster prefab into the 3D viewport -Drag the player prefab from the scene view to the target slot of the monster's monsterAI.lua script. -Run the game -Monster runs on spot and doesnt attack the player unless I am really close and even then I need to shoot the monster to initiate the attack... I'm probably missing something obvious, but this is strange nevertheless. The desired result is that the monster(s) will find and run towards the player and attack the player. All help appreciated!
  18. So I have been working for a few weeks on making and fixing up this code, and am at an impasse. I have the weapon equipping and swinging, but nothing is happening to the monsters. I have the code broken into 2 parts, 1 for the swinging and equip, and 1 (attached to a collision entity on the weapons blade) that when it collides with an enemy ai it is supposed to remove health from them. The second code I also plan on tweaking to include everyone who touches it (for traps) but have no clue. The code that isn't working is here. import "Scripts/Objects/Player/FPSWeapon.lua" Script.damage=10 function Script:Start() self.enabled=true end function Script:Collision(entity, position, normal, speed) if self.enabled then self.component:CallOutputs("Collision") end end function Script:Fire() local enemy = self:FindScriptedParent(Collision.entity,"Hurt") if enemy.script.health>0 then enemy.script:Hurt(self.damage,self.player) end --Blood emitter e = self.emitter[2]:Instance() e = tolua.cast(e,"Emitter") e:Show() e:SetLoopMode(false,true) e:SetPosition(pickinfo.position+pickinfo.normal*0.1) e:SetVelocity(0,0,0) end I brought in collision code directly into it so it would know what it was looking for, and imported the script for fps weapon to get the damage system working. It is showing no errors but at the same time doesn't do anything. Any Ideas? I just want to get it working, so I can tweak it for each weapon and trap.
  19. Hello again, I hate bothering you guys with what seems to be bacis stuff. I need some help editing or writing my own code for ai and melee weapons. I have their animations all imported (both weapon and monsters) and have set up some basic scenes to test them. I took a look at the prefab monster code as well as the prefab fps weapon code. I thought simply changing the code with my own values and equipping them would work for now, but it doesn't. I dont even know where to begin. Is there a tutorial for a basic ai and melee weapon system? Lua or c++ would work for it. Any packages possibly for me to take a look at? I am sorry so very lost.
  20. To prevent non Leadwerks Engine specific/Related entries from my Blog being automatically published here via the Blog Feed, I have removed that functionality, and will simply add a new entry title and link here for all Leadwerks Engine specific/Related entries. Leadwerks Related Blog Entry. Leadwerks 3.2 Indie : Proof Of Concepts & Several Tests.
  21. Working on two principles K.I.S.S. (keep it simple stupid!) and more for less. I spent a lot of time researching (read playing) certain games that have what I call an ambient NPC population. This is specifically those NPC's that reside in the background and generally have no real or very limited interaction with the player. Two such areas of research <coff> included Assassins Creed and Fable 2. Although both of these games have a far more complex general NPC population than what I am setting out to create, but the "research" was most enjoyable. First Step: Machine Intelligence "Kinda Almost": Machine Intelligence "Kinda Almost", simply gives an illusion that there is intelligence at work but there is not, sort of like, the lights are on but no ones home. There are several subsets to this group but the obvious one is simple animation. Also this can include proximity reaction behaviour, a posh and complicated way of saying when the player is near, stop what you are doing, and do this instead. But that's as far as interaction goes for those NPC's in this group. This simple ambient NPC type could also use lua scripts, loaded with the level and be left to attend to themselves. For example, just two chaps having a chat: Having a chat is basically all they do, its simple per character animation's controlled by lua scripts. The purple block volume is the walkable area that would be utilised by more "advanced" ambient NPC's, typically those that move about, particularly those that would use Pre-Loaded and Pre Calculated path routes for this particular level. The purple block volumes are simply required by the application I wrote to generate the route node data for the pathfinding tools I created and have demonstrated here. Not that this is a level just something thrown together for testing but I think I should unarchive some of my GMF format models and put together a small town/Village for testing this, rather than another simple "BlocksVille". There are obvious limitations to consider with this approach to the first tier of the Ambient NPC population for a "busy" town/village. The NPC's need animation's, most come with maybe 15 animation's (if you're lucky). My intention is to surmount this by adding animation's to them, primarily Mo-Caps. This will require a little work but will be the bedrock for the construction of this "tier". There are some very good applications on the market for achieving this. For me I prefer Fragmotion, there is also a free alternative that I also use on occasion called Pacemaker. But don't be fooled though, none of these applications will do it all "for you", you will need to skin the mesh to the correct rig for the plethora of free to use Mo-Caps, or indeed ones that can be purchased. It will all depend on budget and or requirement. This is still only a very basic outline for this "packing" stage, it will still require a lot of work but my goal is to get the impact of it at runtime to an almost negligible effect even when including proximity reaction behaviour.
  22. Following on from "On The Right Path, A* Pathfinding In Leadwerks" I thought before I archive the code, I'd have a little "dabble" with a basic function to get an NPC reading the path data and then following it to the "target" I had a couple of ideas on how to do this but as time for playing in leadwerks was running out I opted for a basic idea. 1. Target Position Selected 2. Path from NPC to selection calculated. 3. Path transfered to a temp storage array for that particular NPC 4. An NPC Pilot reads that data. 5. NPC follows the pilot. Think of the "pilot" as the plastic stick thing on the underside of a scaletrix car .. it follows the groove in the track and the car follows right along. This was the quickest and dirtiest method but solved a variety of issues in one. Plus I just wanted to get the NPC moving and the sands of time were running out. So an hour later and I have not only the NPC following the generated path but the path is updated with a repositioning of the target and the pilot takes the NPC off onto the new track. This is all still quick and dirty, when I can remember what the code does and does not need, I will up a small demo.exe to the thread Pathfinding With A* EDIT: I have uploaded a demo.exe here : Hide And Seek Demo
  23. Well, it has been a while since I made a blog entry, let alone done any work in LE. But I do keep an eye on the forum and Leadwerks in between Daily life and working on my project(s). I have also been going through my Blitzmax code archive, where I stored all my Blitmax/LE projects and code snippets (finished and not completed). One idea I had been playing with before, was basic NPC AI. I had some small success with some Bugs using autonomous logic. See Here In my "treasure trove" of unfinished and messy development code I found what was going to be the next step (for me) with AI functionality. The dreaded "pathfinding". I have read that Naughty Alien said he will be doing a two part tutorial on this and I am looking forward to that, having followed his work with Hidden Dawn I expect the tutorial to be top notch. But I wanted to get to grips with the in's and out's myself. So I started with the basics. I spent some time looking for code snippets and A* lib's in any language that had them to see of I could follow along what was being done ... Not a good place to start, in the end I spent some time reading "A* Pathfinding for Beginners", very helpful.I decided it would be best to start with a "2D" approach and maybe use nodes. Most 2D implementations use a grid based on the X and Y co-ordinates of the screen and tiles to represent an "unwalkable" tile. So I thought I would translate this approach to a 128 x 128 flat terrain with some primitive models to define the "unwalkable tiles". As this was a time limited "side-track" in LE from my other projects, it was all going to have to be quick and dirty. I wrote an app to calculate which "tiles" of the 128 x 128 terrain did not have an obstruction and then squirted this data out to a text file to be read into another app as a 2D array. The map data loads in a few milliseconds so this eliminated having to wait for the same data to be generated by the application I wanted to use to try and get the pathfinding working. One problem I did encounter was that I ended up trying to reconcile three different centre of origins for the co-ordinates. The map data had 0,0 at the top left, the terrain of course has 0,0,0 in the centre and my maths managed to get the A* to read 0,0 at the bottom left, after some profanity meditation I used some quick and dirty and hastily constructed math functions to bring them all into focus, thus as per usual the dev. code was a complete mess, atleast on that score I am consistent lol This version of pathfinding is very limited, mainly due to the way I created the node info. But for a first attempt I was quite happy. I have uploaded a demo App to the showcase , the picking can be a little temperamental and sometimes the quickest path is not always found and some "sightseeing" seems to find its way into the route. But on the whole I think it is definitely a platform on which to build.
×
×
  • Create New...