🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!
A Limitless Interaction Caller Engine (A.L.I.C.E) is a high-performance interaction engine that can be used for physics, missiles, and much more. The core of the system is heavily optimized for speed and the outer shell provides an easy interface that allows any type of interaction to be set up quickly. ALICE makes full use of Lua's dynamically-typed nature, allowing you to pass anything from units, destructables, items, and your own classes as objects into the system.
Bundled with ALICE are multiple, ready-to-use libraries, the Complementary ALICE Templates (CATs). These libraries allow you to easily create various types of systems with a great deal of flexibility in your designs. You can create missiles that intercept other missiles, fireballs that set trees on fire as they travel to their target, or rocks that roll over the ground and collide with other objects with a never-before seen realism in Warcraft 3. And once you're ready to move past the limitations of those libraries, the ALICE API is waiting for you!
ALICE has an extensive debugging API, where you can select its interal objects in-game to see their properties and efficiently investigate why they are misbehaving.
How to use
The main task when working with ALICE is creating actors, its main internal object, and their interaction functions. An actor is a class that is attached to any type of object and interacts with other actors that were added to the system. Actors carry identifiers and function interfaces determining their interactions and with which other actors they form a pair.
The properties of actors are fully encoded in the table that is used to create them and the interaction functions you write. ALICE takes care of the entire control structure and most of the clean-up, so you can focus on the fun parts of coding. For example, here is the code used to create the reflective barrier that Jaina uses in the showcase video:
Lua:
local function ReflectProjectile(barrier, shell, x, y, z, normalSpeed, __, __)
local phi, theta = ALICE_PairGetAngle3D()
local shieldEffect = AddSpecialEffect("Abilities\\Spells\\Undead\\ReplenishMana\\ReplenishManaCasterOverhead.mdl", x, y)
BlzSetSpecialEffectZ(shieldEffect, z)
BlzSetSpecialEffectYaw(shieldEffect, phi)
BlzSetSpecialEffectPitch(shieldEffect, -theta + bj_PI/2)
BlzSetSpecialEffectAlpha(shieldEffect, math.min(128, normalSpeed // 2))
BlzSetSpecialEffectScale(shieldEffect, 1.2)
DestroyEffect(shieldEffect)
end
---@class ReflectiveBarrier
ReflectiveBarrier = {
--ALICE
identifier = "reflectiveBarrier",
interactionFunc = {
projectile = CAT_EntityCollisionCheck3D
},
radius = 200,
bindToOrder = "starfall",
zOffset = 50,
--CAT
onEntityCollision = {
shell = CAT_EntityBounce3D,
other = CAT_EntityDevour3D
},
onEntityCallback = ReflectProjectile,
mass = 500,
collisionRadius = 160,
elasticity = 0.2,
}
ReflectiveBarrier.__index = ReflectiveBarrier
local function CreateReflectiveBarrier()
if GetSpellAbilityId() ~= FourCC("Ashi") then
return
end
local barrier = {}
setmetatable(barrier, ReflectiveBarrier)
local u = GetSpellAbilityUnit()
barrier.anchor = u
ALICE_CreateFromClass(barrier)
end
Do not get intimidated by the complexity of this resource! While an understanding of the ALICE API is helpful, it is not necessary to build your first systems with it, as ALICE is packaged with ready-to-use libraries.
Motivation
Although the ALICE core was based on my particle system, my motivation for the development of this system was to create A.I. with it, where the interactions would be objects on the map sending data and being evaluated by a bot, who uses this data to calculate its next action. The endless possibilities of Lua soon got me hooked and I started expanding the system and now it is far beyond the scope I had originally intended (I can't stop, please send help!).
I realized that this engine could be used to solve one of the problems that has bugged me in Warcraft 3 for a long time - that you have to treat your own objects (such as particles, missiles) inherently differently from Warcraft 3 objects, since there is no native API, no enum functions, for them. Lua's dynamically typed nature provided an opportunity to remove this separation.
Unfortunately, the heavy reliance on dynamic typing means that ALICE cannot be translated into JASS. However, an interface for Lua-GUI would be a possible in the future.
Features
Feature
Description
Complementary ALICE Templates
There are two ways you can work with ALICE. You can write your own systems with the ALICE API or use the ready-to-use libraries, the CATs. These libraries feature a number functions that you can use to create various objects and their interactions. They provide a good starting point for anyone who is learning the engine. Thes CATs include:
Units/Destructables/Items: These templates help to interface ALICE with the respective object type and are highly recommended when using ALICE with that object type.
Entities: This template contains several functions that help with creating and managing entities. We define entities as objects represented by a table with coordinate and velocity fields and a special effect, so these could be missiles, projectiles, bouncing balls etc.
Missiles: This template includes a several functions that move or accelerate entities, allowing for the creation of missiles or projectiles. It also includes the terrain collision function.
Collisions: This template contains various functions to detect and execute collisions. Collisions can occur between entities and other entities or between entities and units, destructables, or items.
Effects: This template contains various auxiliary functions to make your entities look nice. It includes functions that orient the special effect such that the object resembles an artillery shell, a homing missile, or a ball rolling over the ground, among other.
Ballistics: This template includes a function for realistic, ballistic and sliding movement for entities as well as helper functions for ballistic movement.
Forces: An advanced library that allows you to create interactions where objects are pushing or pulling other objects in the vicinity, such as a black hole sucking in nearby units, or a mage creating an explosion that pushes away nearby projectiles.
Core API
The Core API consists of functions that govern the creation and destruction of ALICE objects, the actors.
Pair API
The Pair API includes various functions that you can use within your interaction functions to quickly create complex interactions. The objects from the interaction currently being evaluated are implied input arguments for those functions, which is why they cannot be used from outside of ALICE. The Pair API includes math functions as well as various utility functions that allow you to put cooldowns on effects or create and clean-up effects on creation and destruction, among other.
Debug API
ALICE features an extensive Debug API. By typing "downtherabbithole", you can enable debug mode and select objects to view information about the attached actors. While selected, all interactions are shown by a green lightning effect whenever they are evaluated. You can combine the ALICE debug mode with Eikonium's Debug Utils to execute code directly on the selected actor. To use debug mode efficiently, always declare your interaction functions as global variables, so that their name gets displayed correctly within the actor tooltip.
Enum API
The enum functions work just like the enum natives, but can be used to efficiently enumerate any objects, including entities. While the enum functions are significantly slower than their native counterparts, the fact that they compile the objects into a Lua table instead of a group almost cancels out the speed difference, and even makes them faster in some situations.
Self-Interaction
Not all interaction functions that we want to call with ALICE should be between two objects. Sometimes, we just want to just execute code periodically on a single object. ALICE allows for that with the convenient self-interaction function table that you can add to actors on their creation. Self-interactions can also be added or removed later on.
Performance
Unlike most other similar systems, ALICE's core avoids using enum functions entirely. Most missile systems, for example, would search for collisions with units by calling GroupEnumUnitsInRange repeatedly. But while natives are optimized and written in lightning-fast C++, Lua is a fast language itself, and calling natives is expensive. For example, GetUnitX, which presumably does nothing but look up the number in a field, is about 10x slower than a Lua function call and 50x slower than a Lua table lookup. In addition, the process of calling the natives repeatedly is inefficient itself.
Think of it this way: Calling GroupEnumUnitsInRange on every iteration is like having the most advanced GPS system there is installed in your car, but then determining the time until arrival by having a kid on the back-seat ask "Are we there yet?" over and over again.
ALICE makes use of two different optimization methods:
Variable interaction intervals: Updates between two objects don't necessarily have to be performed every step. If you want to check the collision between two objects that are two screens apart, you don't need to check again for quite some time, so doing another check on the very next iteration would be a waste of resources. The interaction interval is specified in the interaction function.
Cells: The map is divided into a grid of cells. Objects only interact with other objects that share a cell. This optimization is similar to the quad-trees used for collision checks in Warcraft III, but simplified.
The main advantage of this approach is that ALICE can treat all objects that are passed into it in the same way (units, destructables, items, and entities). ALICE can do not only performant missile-unit collisions, but also missile-missile collisions, and will blow every other system completely out of the water when it comes to the latter task.
For an ALICE missile system, collision detection will be only a minor fraction of the computation time of each missile in most situations. As long as graphical rendering is not a problem (missiles are spread over the entire map), ALICE is able to compute 2000+ missiles at the same time without the need to skip computation of missile instances.
Installation
Copy the ALICE Config and Script section into your map. The rest of the ALICE files are documentation and it is up to you if you want to import them or not.
Next, make sure you have all the requirements imported. These are:
Required for the Units, Destructables, and Items CATs, which are highly recommended inclusions.
WidgetType
This handy little library allows ALICE to determine the correct types of the objects passed into it. Since it caches the result, it is fast and efficient. Included in the test maps.
Highly recommended if you want to do any kind of 3D interactions with ALICE as it improves performance and ensures synchronicity in multiplayer. Otherwise not needed.
CustomTooltip.toc
CustomTooltip.fdf
These are the frame definition files for the actor tooltips in debug mode. Included in the test maps.
Complementary ALICE Templates
Lastly, import the CATs that you want to work with. These are:
CAT
Description
Widgets
Requirement for Units, Destructables, and Items CATs.
Objects
Requirement for Entities, Missiles, Collisions, Ballistics, and Forces CATs.
Units, Destructables, Items
Automatically create and destroy actors for objects of the respective type.
These libraries contain various functions that you can use in the definitions of your objects.
Getting Started
After installing ALICE and its requirements, preplace some units, trees, and/or items and launch your map. Type "downtherabbithole" to enable debug mode. Now you can click on any object to view the actors attached to it. A tooltip with all the relevant information should pop up when you do. These actors are created by the respective CATs.
The actor's description lists all of its identifiers. These identifiers are important when we create interaction functions to tell ALICE which type of objects they should affect. The tooltip should also tell you that these actors are currently unpaired. Actors created by these CATs are passive; they only receive, but do no initiate any pairs.
As you move a unit around, you can see how the actor moves along with it, and how the cells it is currently in are being updated. Interactions can only occur if two actors have one or more overlapping cells.
To initiate pairs and create interactions, we need to create new actors that pair with the passive actors attached to the units, destructables, and/or items in your map. The test map and cinematic maps include many examples on how to create those types of actors.
Return values in interaction functions are now optional. If the return value is omitted, ALICE will automatically put pairs using that function into the optimized EveryStep cycle.
Added API functions to add or remove self-interactions.
Added the Matrix API. These functions can be used to write data tables and share data between object pairs.
ALICE now uses the WidgetType library to infer the correct type of objects passed into it.
To improve performance with widgets, Math API functions now use cached values for object coordinates that get reset each cycle.
Freeze protection is now based on evaluation time instead of number of pairs.
ALICE_Benchmark can now be used to view the evaluation time of each cycle.
Added the HALT_ON_FIRST_CRASH option in the config.
Reorganized API into Core and Advanced API.
To-Do List
Fix bugs related to ballistic movement around cliffs.
Add 2D versions of Collisions and Forces libraries.
Add a better knockback system.
Add line-segment enumeration.
Identify and remove useless features to reduce bloat.
Enable asynchronous code execution.
Credits
Cinematic Showcase Map
Fighting Spirit for Spellbringers
Edited by Vinz
Dwarf Grenadier Model by Maximal
Fire Models by Vinz
Bullet/Muzzle Flash Models by Vinz
Arcane Explosion Model by Suselishe
Starsphere by ILH
Battlecruiser and Carrier Model Imports by Daratrix
Plasma Blast Model by nGy
Missile Model by Vinz
Thanks Yea, it's a bit too technical mumbo-jumbo right now, but I hope there can be a GUI-friendly version in the future, or something built with it that is GUI-friendly.
Saying this resource is extensive is a severe understatement. I am glad to see someone else using spatial hashing... The performance you can get from these algorithms are amazing. I actually have the problem where the effect models themselves are more expensive to compute than the collision/pathing logic. With that being said we can reduce the number of table lookups from table[x][y] to table[index]. You can do this by doing:
index = x + y * 1e7 (works for any map size)
Edit:
You should consider doing effects all the way down. I've done it and the performance increase is stupendous.
Saying this resource is extensive is a severe understatement. I am glad to see someone else using spatial hashing... The performance you can get from these algorithms are amazing. I actually have the problem where the effect models themselves are more expensive to compute than the collision/pathing logic. With that being said we can reduce the number of table lookups from table[x][y] to table[index]. You can do this by doing:
index = x + y * 1e7 (works for any map size)
Thank you, yes, the performance increase after I added the cell optimization was quite substantial. With my vJASS version, I was hardlocked at 181 objects (sqrt(32768), the max array size), but I estimate the maximum I could do without that restriction was 250. After translating it to Lua, I went up to 1000, so a ~16x performance increase. Then, after adding the cells, I went to 3000, so another ~10x performance increase, but probably much more, because, like you said, at such a high number of objects, rendering and moving the objects starts to take the majority of the resources.
You're right that I could improve the performance by doing linear indexing. However, I have to make sure that there are no bugs and I don't want to change anything about the core, because after converting it to linear indizes, the code becomes quite unreadable. Currently, I'm focused on getting the API to always work in the way you'd intuitively expect it to. I'm creating different scenarios and when I encounter a problem I can't solve, I change the engine such that I can.
I'm also not sure if linear indexing would make sense everywhere, because the math to get the index could be slower than the table lookup itself. I'd have to make some tests. Did you do tests on that?
Edit:
You should consider doing effects all the way down. I've done it and the performance increase is stupendous. View attachment 468609
Holy ****, that's a lot of skeletons! With effects all the way down you mean replace units etc. with special effects? Well, I did make a map where there's like 5 units on the map and everything else are special effects... But, of course, that's not always feasible.
Is there a way to automatically assign an actor for a destructible upon its creation? I can do it with unit by using a Unit Indexer library, but so far there is no function to detect a destructible entering the map if I am correct.
Does your system work on particles with different collision sizes and masses? I mean does it work if I have two spheres with radii 10000 and 100 colliding with each other?
Is there a way to automatically assign an actor for a destructible upon its creation? I can do it with unit by using a Unit Indexer library, but so far there is no function to detect a destructible entering the map if I am correct.
Yes, there is no trigger to detect a destructable entering, but, in the destructables library, I created hooks to create actors when the CreateDestructable functions are called. On map initialization, I loop over all destructables. A destructable being revived won't create one, though. That's a problem I haven't solved yet.
Does your system work on particles with different collision sizes and masses? I mean does it work if I have two spheres with radii 10000 and 100 colliding with each other?
Yes, that's not a problem. You can see in the video I posted above your post that there are rocks with different sizes and masses colliding with each other.
You can store the collision sizes of your particles in the table, then in your collision function do:
Lua:
local distance = ALICE_PairGetDistance()
if distance < particle1.collisionSize + particle2.collisionSize then
DoStuff()
end
However, the engine also needs to know at which range it should start checking for collisions. That's done with the .radius field. You can take a look at the Cells example in the showcase map to see how that works.
hmm.. like playing warcraft with 3d camera movement, but not always losing sight when walking under giant trees or the camera falling when walking through the hills?
hmm.. like playing warcraft with 3d camera movement, but not always losing sight when walking under giant trees or the camera falling when walking through the hills?
Hm, I'm not that far yet. Currently looking into ways to accurately track the mouse movement so you can move the camera with your mouse like in WoW. But you're talking about using the engine for the camera movement? If so, that's difficult, because camera positions are not synced and the engine needs to stay synced.
Do you have a map in mind that has that camera movement you're describing?
But you're talking about using the engine for the camera movement? If so, that's difficult, because camera positions are not synced and the engine needs to stay synced.
no, i meant not the camera thats moving, but my movement with 3rd camera.. sorry for my english.. maybe you can adjust the camera's distance of view when you near small trees / big trees / giant trees with accurate size of the trees or just simply hide the trees.. and adjust camera's height/offset when you walk on low/high ground with accurate height of the ground and the camera.. due to respect, you are the wizard of this matters.. im just a person who is always amazed by magic..
Do you have a map in mind that has that camera movement you're describing?
no, i meant not the camera thats moving, but my movement with 3rd camera.. sorry for my english.. maybe you can adjust the camera's distance of view when you near small trees / big trees / giant trees with accurate size of the trees or just simply hide the trees.. and adjust camera's height/offset when you walk on low/high ground with accurate height of the ground and the camera.. due to respect, you are the wizard of this matters.. im just a person who is always amazed by magic..
Ah, I see what you mean now. I totally forgot about the tree transparency thing in my video.
Yes, when you have a 3rd person camera like in WoW, it "collides" with objects and walls and goes around them. So, you'd need some kind of collision detection with the camera and the objects. Same thing if you want to make the objects transparent. The example in the video is really simple compared to a 3rd person camera in a 3D world. It's also based on the position of the hero and not on the position of the camera.
The collision detection could be done with ALICE. You would create an object that represents the camera that moves around the world. I would just need to upgrade the engine to allow interactions to be executed asynchronously if it's for a multiplayer map. One player has the camera over at location A and the camera is trying to go around tree B, the next player has the camera somewhere else and doesn't see the same collision detection being performed. This must not cause a desync.
That's my current thinking. Of course, there could be easier ways that I haven't considered yet.
sorry, no longer have the maps, but Shadow of the Past and Daemonic's sword is not bad..
as i remember, they have 3rd person camera, but you know they are old maps, so i dont want to judge or anything, but sometimes i need to return the camera view back to normal view during gameplay ..
The collision detection could be done with ALICE. You would create an object that represents the camera that moves around the world. I would just need to upgrade the engine to allow interactions to be executed asynchronously if it's for a multiplayer map. One player has the camera over at location A and the camera is trying to go around tree B, the next player has the camera somewhere else and doesn't see the same collision detection being performed. This must not cause a desync.
that what i was talking about .. camera's distance get closer to the hero, if there a wall or trees behind the hero, but not change the view front the hero.. i mean it will change the field of view..
as i remember, they have 3rd person camera, but you know they are old maps, so i dont want to judge or anything, but sometimes i need to return the camera view back to normal view during gameplay ..
Well, hopefully a mouse-controlled camera doesn't turn out to be too jank, so RPG maps can use that . With such a camera, you would also need WASD movement then. I can't get over the fact that none of these RPGs have their own movement system (at least drag-move) and it's just clicking everywhere while your hero is constantly spamming "A sound plan." "Of course." "For honor." "A sound plan." and so on.
Well, hopefully a mouse-controlled camera doesn't turn out to be too jank, so RPG maps can use that . With such a camera, you would also need WASD movement then. I can't get over the fact that none of these RPGs have their own movement system (at least drag-move) and it's just clicking everywhere while your hero is constantly spamming "A sound plan." "Of course." "For honor." "A sound plan." and so on.
Return values in interaction functions are now optional. If the return value is omitted, ALICE will automatically put pairs using that function into the optimized EveryStep cycle.
Added API functions to add or remove self-interactions.
Added the Matrix API. These functions can be used to write data tables and share data between object pairs.
ALICE now uses the WidgetType library to infer the correct type of objects passed into it.
To improve performance with widgets, Math API functions now use cached values for object coordinates that get reset each cycle.
Freeze protection is now based on evaluation time instead of number of pairs.
ALICE_Benchmark can now be used to view the evaluation time of each cycle.
Added the HALT_ON_FIRST_CRASH option in the config.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.