User Manual
Download and Open the Simulation
For this project the following Unreal Engine version is needed: at least 4.14.x. In addition the Runtime Mesh Component plugin must be installed.
Java 8 must be installed on the system. It is important that your Path to mysiccom.jar doesn't contain any spaces. An IDE Microsoft Visual Studio 2015 (VS14) is highly recommended.
This simulation has been developed and tested on the following system:
| Processor | RAM | Operating System | GPU | Add. Requirements: |
|---|---|---|---|---|
| Intel Core i7-6700K @ 4.00 GHz | 64.00GB | Windows 10 Enterprise - 64bit | NVIDIA GeForce GTX 1080 | 1x USB-3.0-Port, HTC Vive |
Simply double click the project file (.uproject) in the folder, wait for Unreal Engine to open and press "Play" in the top bar of the window.
Ingame Controls
Use the Mouse to look around. Left Click to open the menu and select with your mouse the desired button. Left click again to chose or close the menu.
Time of Day control:
Toggle FastForward T
Increase speed P
Decrease speed O
Movement:
Swim Forward: W
Swim Backward: S
Slide Left: A
Slide Right: D
Control Camera: Mouse
Swim Up: Left Shift
Swim Down: Left Control
Other:
Toggle Flashlight F
Toggle HUD Icons I
3D Radial Menu Left Click
To look around you simply move your head while equipped with the HTC Vive.
Movement:
Swim Forward: Backside Button of Right Controller
Swim Backward: Backside Button of Left Controller
Control Camera: Use your Head
Other:
Toggle Flashlight Press Touchpad on Left Controller
Menu:
3D Radial Menu Press Touchpad on Right Controller
Navigate Move Finger on Pad
Choose Click on Pad while on Button to choose
Close Click on Pad while in the Center
The (VR) Ingame Menu
For the menu we used icons from other sources. These may be found here.
Complete List of Features
An immersive lifelike coral reef based on the flora and fauna of a Zanzibar reef
Created for an Virtual Reality experience
Algorithmic birth, growth and decease of corals based on data received from the integrated SICCOM plugin (see paper below)
Over 40 selfmade models
4 different kinds of generated corals
3 different modeled corals
2 types of algae
Textured and animated
10 different types of animals
including fish, predators, crabs, turtles, stingrays, starfish
Every model is textured and animated
Rocks, wrecks, anchor and many more static objects
Generated corals and algae fight for dominance within the reef
They also react on parametrical changes such as a change in temperature
Swarm algorithms simulate different behaviour of fish and predators (domain-driven design [DDD])
Different states like "Travelling", "Hungry" or "Hunting"
They can be found either in swarms, small groups or alone
Sharks hunt in packs, fish try to regroup after a predator attacked
Fish nibble on corals, making them bleach
Feature-rich ingame (VR) menu
Simulation Speed / Reset Simulation
Change Water Temperature
Change Speed of Day & Night Cycle
Toggle Fog / View Distance
Reset Player Position
Volume
Show Information on current numbers of simulated algae & corals
Change Movement Speed
Explore the reef at night with a (VR) flashlight
Selfmade features to increase realism
Water visualization
Godrays
Caustics
Day & Night Cycle
View Distortion
Ambient underwater noise
Particles
Some fish have randomly chosen textures
Intuitive movement with VR-Controller or Keyboard
Some models use Parallax Occlusion Mapping for a more realistic appearance
Triggerable event: Pulling an anchor through the reef, destroying generated corals and algae in its path
Fish disappear realistically at night
Project Planning
Working with Scrum
For the master's project "VR CoralReef" we chose the Scrum model for managing the various tasks that approached each one of us. These tasks are stored in a Kanban board which we gradually implement. The whole group is responsible for updating the board. An important ritual are regular meetings where the whole group creates tasks and guesses the estimated amount of work (in points) for them, while each one contributes his or her individual skills and knowledge to the discussion. This ensures, that every member of the group is fully aware of each duty and its complexity.
For Scrum we chose a sprint length of two weeks, in which we complete said estimated and assigned tasks. In addition to the planning meetings regular retroperspectives are held in order to identify issues within the group as early as possible. Each end of a sprint privdes that we present our solutions to those responsible for the masters project and collect feedback (which is a phase also noted in the Kanban board). Subsequently we create a new set of tasks for the next sprint. While doing so, we try to keep the amount of work/point close to the project average so far.
Another fancied ritual for quality assurance is a regular code review where each group member chooses a part of code he or she wants to present and improve.
Development process
In order to visualize the process developer and reviewer undergo, we created an abstract diagram of the development process.
Code Architecture and Diagrams
(free for non commercial use only but not commercial use -MIT License-)SICCOM Plugin
The "SICCOM" Manager from the ZMT delivers raw data on birth, growth and decease of corals and algae, which are also fighting each other for dominance within the reef. The "VR CoralReef" takes this data directly and visualizes it as realistically as possible. Two of our members worked on a plugin for the Unreal Engine 4. Download the PDF below.
Swarm algorithms
We chose to base the algorithms of swarms, predators and lone fish on domain-driven design (DDD) - as far as practicable - in Unreal Engine 4. We could successfully discard the secondary ports, since UE4 already provides everything necessary. All algorithms work basically the same way, though the predators and lone fish own additional states, since they - unlike swarms - show varying behaviour.
The following diagrams describe the process per frame. Function calls are called by name and parameters, content of functions on the other hand only by abstract summaries.
Each frame triggers a tick event with the time that passed. This event selects the Actor class in the primary port in the adapter, which can seen in the diagram to the far left. These forward the events with necessary parameters to the manager class in the administration layer. The task of that manager class is to run the operations inside the domain in the correct order.
Until the implementation of the domain logic one can see, that the adapter and the administration layer are implemented mostly analogue. Inside the domain the functionalities begin to vary more, except for calculations of direction and position of the fish. For swarms these calculations are for social and individual forces and the potential occuring flee direction from predators.
Inside the domain, predators have states which influence the behaviour greatly, even though functionality of these states has been kept as low as possible. The two states are called "Travelling" and "Raiding". Each frame the administration triggers an event, deciding to stay in the current state or changing to the other one. A group of predators acts analogously, since they share states. Either they travel together - or they hunt together. Processing the forces of predators is simpler: since they ignore social forces, they focus on individual ones and hunting direction.
Individual/Lone fish have states inside the domain, which affect each of them individually. Since the states of individual fish differ more (compared to predators), they need states which can manipulate the fish completely. Until this point the individual fish travel through the reef or scrape on corals. Each fish decides individually, which coral it scrapes on. A synchronization is not happening. Once the set time for a state has passed, the fish will move on to the next state.
Ground Animals
Inside the Domain ground animals have a StateMachine, which analyzes - depending on its current state - the surrounding area of the animal for influencing factors. These factors can be objects standing in the way, other ground animals or the player. For each of these cases there are specific states, which alter the direction of movement. In case of a collision, for instance, a new path is calculated. If the player is close, ground animals can become aggressive or hide. Following the viewing direction and rotation of the ground animal will be adjusted depending on the alteration of movement.
States in general
The states of individual fish and predators work the same way. They have states switching into other states as soon as a set individual timer runs out. Until run out, the states receive constantly updated information on their respective movement direction. The moment a state runs out, it receives information which state is set next.
State transitions
There are three kinds of transitioning states:
The first group consists of "ScrapingTravelling", which makes the fish swim around until the "hunger" kicks in, "Scraping", which makes the fish dock and feed off a coral and "ScrapingUndocking", which makes the fish undock slowly until it finds himself in "ScrapingTravelling" again.
The predator logic can be summarized in a second group: "Travelling" makes them swim around in the simulation until several predators unite into the state "Raiding", attacking a swarm of fish until they go back to "Travelling", once "satisfied".
The last group consists of "Travelling" and "Center". These states make fish swim around and rotate them slowly towards the center of the simulation in order to make them not swim out of area.
Swarms.Administration
update(List fishes, float deltaSeconds)
for(Fish f : fishes)
f.calcSocialForces(fishes)
f.calcIndependentForces()
f.calcAndUpdateVelocity(deltaSeconds)
f.calcAndUpdatePosition(deltaSeconds)
Swarms.Domain
calcSocialForces(List fishes)
this.sumSeperationForces(fishes)
this.sumAlignmentForces(fishes)
this.sumCohesionForcesForces(fishes)
this.normalizeForces()
Swarms.Domain
sumSeperationForces()
Vec3 sumSeperationForce
for(Fish f : fishes)
if(f.position.distTo(this.position) < range)
sumSeperationForce +=
f.position-this.position
Swarms.Domain
sumAlignmentForces()
Vec3 sumAlignmentForce
for(Fish f : fishes)
if(f.position.distTo(this.position) < range)
sumAlignmentForce += f.direction
Swarms.Domain
sumSeperationForces()
Vec3 sumCohesionForcesForce
for(Fish f : fishes)
if(f.position.distTo(this.position) < range)
sumCohesionForcesForce +=
this.position-f.position
Swarms.Domain
calcIndependentForces()
this.calcTerrainAlignmentForce()
this.calcTerrainRepulsionForce()
this.calcSpeedAdaptionForce()
this.calcPitchDampingForce()
this.calcRandomForce()
this.calcAttractionPointForce()
this.calcFleeFromPredatorsForce()
Swarms.Domain
calcAndUpdateVelocity(float deltaSeconds)
this.calcCombinedWeightedForce()
this.velocity = this.velocity + combinedForce * deltaSeconds / this.mass
this.direction = this.velocity.normalize()
Swarms.Domain
calcAndUpdatePosition(float deltaSeconds)
this.position += this.velocity * deltaSeconds
Predators.Administration
update(List predators, float deltaSeconds)
for(Predator p : predators)
p.updateState(deltaSeconds)
p.updateVelocity(deltaSeconds)
p.updatePosition(deltaSeconds)
Predators.Domain
updateState(float deltaSeconds)
this.state.updateTimer(deltaSeconds)
if(this.state.timeOver)
swapState()
Predators.Domain
updateVelocity(float deltaSeconds)
this.calcPitchForce()
this.calcTerrainAligmentForce()
this.calcTerrainRepulsionForce()
this.calcStateForce(this.state)
this.calcDesiredSpeedForce()
this.calcCombinedForce()
this.velocity += this.combinedForce * deltaSeconds / this.mass
Predators.Domain
updatePosition(float deltaSeconds)
this.position += this.velocity * deltaSeconds
IndividualFishes.Administration
update(List fishes, float deltaSeconds)
for(Fish f : fishes)
f.updateState(deltaSeconds)
f.updateVelocity(deltaSeconds)
f.updatePosition(deltaSeconds)
IndividualFishes.Domain
updateState(float deltaSeconds)
this.state.updateTimer(deltaSeconds)
if(this.state.timeOver)
swapState()
IndividualFishes.Domain
updateVelocity(float deltaSeconds)
this.calcPitchForce()
this.calcStateForce(this.state)
this.calcDesiredSpeedForce()
this.calcCombinedForce()
this.velocity = this.velocity + this.combinedForce * deltaSeconds / this.mass
IndividualFishes.Domain
updatePosition(float deltaSeconds)
this.position += this.velocity * deltaSeconds






