• 🏆 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!

Generating trees.

Level 6
Joined
Aug 26, 2016
Messages
100
Hello, I am interested in the generation of scenery and landscape, I have one question, in the screenshot there is an area with four trees, how can I create trees in the same position in the neighboring area?
изображение_2024-04-19_171131309.png
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,576
Use the Point With Polar Offset action:
  • Actions
    • Set VariableSet CenterPoint = (Center of Region 000 <gen>)
    • Set VariableSet OffsetPoint = (CenterPoint offset by 256.00 towards 45.00 degrees.)
    • Destructible - Create a Summer Tree Wall at OffsetPoint facing (Random angle) with scale 1.00 and variation 0
    • Set VariableSet OffsetPoint = (CenterPoint offset by 256.00 towards 135.00 degrees.)
    • Destructible - Create a Summer Tree Wall at OffsetPoint facing (Random angle) with scale 1.00 and variation 0
    • Set VariableSet OffsetPoint = (CenterPoint offset by 256.00 towards 225.00 degrees.)
    • Destructible - Create a Summer Tree Wall at OffsetPoint facing (Random angle) with scale 1.00 and variation 0
    • Set VariableSet OffsetPoint = (CenterPoint offset by 256.00 towards 315.00 degrees.)
    • Destructible - Create a Summer Tree Wall at OffsetPoint facing (Random angle) with scale 1.00 and variation 0
Untitled.png

You can also use Point With Offset if you like to work with x/y coordinates.

A For Loop and some basic math would optimize it:
  • Actions
    • Set VariableSet CenterPoint = (Center of Region 000 <gen>)
    • Set VariableSet Angle = -45.00
    • For each (Integer A) from 1 to 4, do (Actions)
      • Loop - Actions
        • Set VariableSet Angle = (Angle + 90.00)
        • Set VariableSet OffsetPoint = (CenterPoint offset by 256.00 towards Angle degrees.)
        • Destructible - Create a Summer Tree Wall at OffsetPoint facing (Random angle) with scale 1.00 and variation 0
Remember that the game is broken up into a grid (press G in the World Editor to see it). A tree takes up 128 "units" on this grid so an offset of 256.00 would be equivalent to an offset of "2 trees". Understanding this grid and the angles (0 = east, 90 = north, 180 = west, 270 = south) will help you a lot.

Also, don't forget to clean up memory leaks:
  • Custom script: call RemoveLocation( udg_CenterPoint )
  • Custom script: call RemoveLocation( udg_OffsetPoint )
You generally want to Set, Use, Remove. However, CenterPoint should be removed at the very end since we need it to remain active for OffsetPoint to work.
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,576
All that remains is to teach you to remember the positions of all deorations in an area when creating them in another
It's not easy and may not even be possible to get this working the way you want. There's very limited functions related to Destructibles, you can't get their variation, facing angle, etc... This is the best I could do:
  • Copy Destructibles
    • Events
    • Conditions
    • Actions
      • -------- Note: You must Set Destructible_Region_First and Destructible_Region_Second before running this trigger! --------
      • -------- --------
      • -------- Copy destructibles in first region: --------
      • Set VariableSet Destructible_Index = 0
      • Set VariableSet Destructible_Center_Point = (Center of Destructible_Region_Old)
      • Destructible - Pick every destructible in Destructible_Region_Old and do (Actions)
        • Loop - Actions
          • Set VariableSet Destructible_Index = (Destructible_Index + 1)
          • Set VariableSet Destructible = (Picked destructible)
          • Set VariableSet Destructible_Position = (Position of Destructible)
          • -------- --------
          • -------- Store the type, distance, angle of the destructible: --------
          • Set VariableSet Destructible_Type[Destructible_Index] = (Destructible-type of Destructible)
          • Set VariableSet Destructible_Offset_Distance[Destructible_Index] = (Distance between Destructible_Center_Point and Destructible_Position)
          • -------- Try to account for floating point error: --------
          • Set VariableSet Destructible_Offset_Distance[Destructible_Index] = (Destructible_Offset_Distance[Destructible_Index] + 0.01)
          • Set VariableSet Destructible_Offset_Angle[Destructible_Index] = (Angle from Destructible_Center_Point to Destructible_Position)
          • -------- --------
          • Custom script: call RemoveLocation( udg_Destructible_Position )
      • Custom script: call RemoveLocation( udg_Destructible_Center_Point )
      • -------- --------
      • -------- Paste destructibles in second region: --------
      • Set VariableSet Destructible_Center_Point = (Center of Destructible_Region_New)
      • For each (Integer Destructible_Loop) from 1 to Destructible_Index, do (Actions)
        • Loop - Actions
          • Set VariableSet Destructible_Position = (Destructible_Center_Point offset by Destructible_Offset_Distance[Destructible_Loop] towards Destructible_Offset_Angle[Destructible_Loop] degrees.)
          • -------- --------
          • Destructible - Create a Destructible_Type[Destructible_Loop] at Destructible_Position facing (Random angle) with scale 1.00 and variation 0
          • -------- --------
          • Custom script: call RemoveLocation( udg_Destructible_Position )
      • Custom script: call RemoveLocation( udg_Destructible_Center_Point )
  • Copy Example
    • Events
      • Time - Elapsed game time is 1.00 seconds
    • Conditions
    • Actions
      • Set VariableSet Destructible_Region_Old = First Region <gen>
      • Set VariableSet Destructible_Region_New = Second Region <gen>
      • Trigger - Run Copy Destructibles <gen> (ignoring conditions)
      • -------- --------
      • Set VariableSet Destructible_Region_Old = Third Region <gen>
      • Set VariableSet Destructible_Region_New = Fourth Region <gen>
      • Trigger - Run Copy Destructibles <gen> (ignoring conditions)
^ This trigger shows how to use my first trigger. You set the two Region variables and then run Copy Destructibles. It'll copy everything in Region_Old and create it in Region_New. It doesn't work perfectly though.
 

Attachments

  • Copy Destructibles 1.w3m
    19.6 KB · Views: 4

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,576
I was assuming this whole time that you were referring to generating stuff at runtime like procedural generation. If you want to do it in the World Editor then simply use Copy and Paste, you can copy literally anything that's placed on the map, just hold down left click, drag, and press Control C.

For copying terrain at runtime, you'd need to set Points at the center of each grid cell contained within a Region and store those in an Array the same way I'm doing it with the Destructible_Types.
 
Level 39
Joined
Feb 27, 2007
Messages
5,024
It's not entirely clear how to do this.
I would probably use a combination of locusted units and destructables-with-no-model-but-with-a-pathing-map to simulate actual destructables. That would allow you to use unit functions for facing/scaling/color/height/etc. and still block unit pathing as necessary.

While locusted units can’t be found with region or range checks, you could still find them by ‘units of type’ (and then check if that unit is in a specific region or whatever). You could also link each destructable-unit pair together using a hashtable, which would allow you to remove the units if their paired destructable is destroyed, or work backwards through ‘destructables in region’ searches to find the units, too.

You should also be aware that that dynamic terrain and destructable generation functions have historically hit the WC3 OP limit easily. The OP limit is higher now but it still exists, so you should be aware of this possibility.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
Terrain generation is very complicated. Some people have spent years, possibly even most of their careers, working on terrain generation for various games.

For overall terrain generation, usually some sort of continuous 2D noise function is used as the starting point. One or more such noise functions with different seeds and scales form the base that then various transform functions work on to generate the final terrain output. This can not only handle terrain tile generation, but also object placement given appropriate transformation functions. The most recent popular example of this is Factorio, but I think RTS games have used it for random maps for decades now.

This sort of purely random terrain can make a good foundation for a large world, but often feels bland and generic. This is why games like the Diablo series prefer to go with bespoke elements that can have random variation and be combined randomly. With this approach a tile set is created, possibly by hand with explicit object positions, that is then used by a layout manager to place them in a logical way. Kind of like a big jigsaw puzzle except where many difference pieces could fit together perfectly in the same place. A good example of this is Diablo II, where after playing it enough you can start to understand the logic used to fit the pieces together, such as some exits always pointing a certain direction relative to the entrance direction.

Then there is parametric generation of an area. This is less a tool to generate massive random terrain, but rather to fill part of a terrain in a stylistic or purposeful way. For example, along a road you might want to place a pavement with trees every few tiles in a city, possibly spawning a bin, walkway or food stand if the stretch is long enough. This approach is used in games like Anno 1800 or City Skylines 2 to generate the clutter to the side of roads.

Although people like William have successfully used continuous 2D noise functions for tech demos in WC3, WC3 is not really well suited for it due to lack of native support/acceleration. As such I would recommend using a combination of the other approachs. It sounds like you might even be trying the parametric generation approach?

Parametric generation can be quite difficult to get setup. A very basic implementation might be a function for each type of generation/fill you want to use, and then either varying the input parameters randomly, or selecting a random function to fill a specific area. How the function is implemented really depends on what you want the fill to be, and each will likely need to be tackled as a separate problem with significant experimentation to get good results. For example, if you want to make a function to spawn a house for a village, you might want to start by filling the floor and making the walls using loops. Then, given the space available, start filling it with clutter. Maybe the house is a big space, so maybe it gets sub-divided into 2 or 4 rooms with doors connecting them. Each of these rooms might select another parametric function to generate the clutter for them, so a bedroom might have bets but a kitchen might have pots and stoves. When the function is called you could supply it with a wall style, such as for elven or human houses, a variable area to work with so it can make different sized houses, possibly a hint for the kind of clutter to use such as "poor" for peasant housing or "rich" for more expensive clutter items to appear. As you make more of such functions, some tasks might be able to be broken out into utility functions that are shared between many such functions.
 
Level 6
Joined
Aug 26, 2016
Messages
100
I want to use something like tiles with pre-prepared objects and then place them randomly, with nature everything is simple, I was able to generate a forest only without ponds and mountains, but trees and grass are generated perfectly, but if you generate a city, then you need to generate a tileset based on the location of houses was drawn in advance in the area and with prepared objects, but I don’t know how to transfer the tileset that I prepared in advance to the area, and with dungeons, I also don’t know how to arrange the rooms correctly..
 
Level 39
Joined
Feb 27, 2007
Messages
5,024
Did you read what I wrote at all? You’ll need to prebuild those tiles, store all the information about them, and then just rebuild piece by piece. As units you have much more information than as destructables, though ultimately it is a lot of data to store and re-create.

You can test ground tiles, too, though I don’t believe you can get variations so that would always be random (or determined beforehand by manual input).

As Dr Super Good shows, this is quite a difficult problem. I’ve only ever seen it done in wc3 once.
 
Top