Tondiraba Venue Manager
Karl Vaba
Preview of the results
I am going to post two links that summarize the project pretty well. This is in case you do not wish to read through the details of the development plan here (or you just did not find the links among the text).
Final build from this course: https://drive.google.com/file/d/1D2eTx9XnFqe4ILoBTfqCboeXP395nHD3/view
Motivation for the project:
Project plan
The idea for this project is to create an interactive model of Tondiraba Ice Hall. As a venue for many events, event organizers need to understand the layout of the building and its rooms, its inventory, how many people can it accomodate, etc. And usually this "scoping out" is done on site. What this project aims to do is reduce the need for doing it on site by providing an accurate model of the building online, of which you can take a virtual tour.
The end goal is to have an interactive model of the venue running in some server, so it could be accessed by the authorized people from anywhere using a browser. The plan can be divided into some main categories:
- Creating the model. Before we can make the model interactive, we need the model itself. Blender will be used as the modelling tool here. Aside from the main structure of the building, probably some additional objects need to be modelled, like chairs, tables, whatever.
- Texturing / making the materials for the building. As this is supposed to become a representation of the ice hall online, it should look nice :). Creating the materials will probably be done using the game engine (Unity) with which the interactivity is created.
- Programming the interactivity. Unity will be used for this purpose. Users should be able to take a virtual tour of the building from a first person perspective. Additionally the building could be viewed from some outer perspective. If you would imagine the "Sims" game, thats kind of the direction it is meant to go. Different rooms in the building should be selectable and information displayed about them.
- Getting the model online. A web build should be made of the project and it should be uploaded to some server which can host it
Milestone 1 (25.09)
Reorganizing / modifying the main construction of the building's model. We spoke to one of the architects behind the ice hall's project and he provided us with their version of the building's model. However, it cannot be used as is. There are some drawbacks, which need to be resolved:
- Their model is just the base construction of the building; it does not contain any inner design elements. For example, there are no rooms inside the building, no walls. Such elements should be created
- Their model is too detailed. It is modelled as a real construction, i.e it has all the building blocks that exist in the real building (beams, concrete slabs, etc.). These will not be visible in the end result and just add to the complexity (and the computing power required) of the model. Thus, unnecessary elements should be removed.
- Parts of the model should be organized so that they would be easy to manage in the game engine. E.g right now, the grandstands are all separate objects, but it would be easier to hide them (for the purpose of seeing the rooms under them) in the game if they were grouped as one.
Example of unnecessary details in the initial model:
In the upper image you can see meshes for the first and second floor. Under those floors were some building blocks (seen in the second image), which wouldn't be visible in the end.
The goal of this milestone would be to have the mesh for the 0th floor around the main hall ready to be exported to Unity.
Result:
Upper image: the basement floor around the main hall before. Lower image: the basement floor around the main hall after.
With the walls of the rooms in the 0th floor created, I can export this part of the mesh to Unity and start adding some visuals and interactivity.
Model of the basement in Unity:
Milestone 2 (9.10)
For this milestone I would like to do some texturing and coding. I will take one room of the basement floor which I have an image of (probably the conference room) and do the following:
- Texture the walls and the floor.
- Make the room selectable, so when you click on it, it will be highlighted in some way.
- Make a first person controller and a free camera controller to explore the space. I will not take collisions into account at this point.
- Make it possible to choose between first person control or free camera control.
Results
There is a small video at the end of the descriptions to illustrate the results.
Visuals of the conference room
The visual reference for the room was taken from http://tondirabaicehall.ee/korraldajale/ruumid/konverentsiruum/ I created a texture for the walls and downloaded a texture for the carpet (Reminder: find the link where I downloaded it from...). Based on the pictures on their website I also modelled the doors and windows in the room. Textures used for the floor and walls (yes, the texture of the wall is just a white background with a black trim at the bottom :) ):
Making the room selectable
For this feature, I created an empty "Conference room" game object, onto witch I added the mesh for the walls and the floor, and also a box collider. Unity lets you detect mouse click events (OnMouseDown()) on objects with colliders attached to them. So in the "OnMouseDown()" button I enable emission color for the materials of the meshes to highlight them.
First person and camera controllers
There are two controllers in the scene. One for the overhead view and one for the first person view. Each of the controllers has a camera attached to it. Switching between the cameras is done by clicking on a UI button. Clicking the button switches which controller is enabled.
public class CameraChanger : MonoBehaviour { public GameObject firstPersonController; public GameObject overheadController; public Text buttonText; private enum CurrentCamera { FIRST_PERSON, OVERHEAD } private CurrentCamera currentCamera; public void Start() { ActivateOverheadControl(); } public void ChangeCameraView() { if (currentCamera == CurrentCamera.FIRST_PERSON) { ActivateOverheadControl(); } else { ActivateFirstPersonControl(); } } public void ActivateOverheadControl() { currentCamera = CurrentCamera.OVERHEAD; firstPersonController.SetActive(false); overheadController.SetActive(true); buttonText.text = "Switch to first person view"; } public void ActivateFirstPersonControl() { currentCamera = CurrentCamera.FIRST_PERSON; firstPersonController.SetActive(true); overheadController.SetActive(false); buttonText.text = "Switch to overhead view"; } }
First person controls: W,A,S,D to move around; moving the mouse to look around. Unity's built-in "Character controller" component was added to the empty "First person controller" object. Player movement script was added to the "First person controller". The mouse look script was added to the first person camera attached to the first person controller object. This was created based on a tutorial by Braceys:
public class PlayerMovement : MonoBehaviour { public CharacterController controller; public float speed = 12f; // Update is called once per frame void Update() { float x = Input.GetAxis("Horizontal"); float z = Input.GetAxis("Vertical"); Vector3 move = transform.right * x + transform.forward * z; controller.Move(move * speed * Time.deltaTime); } } public class MouseLook : MonoBehaviour { public float mouseSensitivity = 100f; public Transform playerBody; float xRotation = 0f; void Update() { float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime; float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime; xRotation -= mouseY; xRotation = Mathf.Clamp(xRotation, -90f, 90f); transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f); playerBody.Rotate(Vector3.up * mouseX); } }
Overhead view controls: W,A,S,D to move around; holding down right click and moving the mouse to rotate the view. The camera is attached to an empty object which acts as the "focus point" of the camera. A and D keys move the focus object (left and right, respectively) along the "right" vector of the object. W and S keys move the object along the axis that is perpendicular to the object's right vector in the XZ plane. One thing that is missing from the controller, which would probably be nice to have, is zooming. I will add it in some later milestone.
public class CameraController : MonoBehaviour { public float speed; public float rotationSpeed; float xRotation; float yRotation; bool movementEnabled = true; void Start() { xRotation = transform.localRotation.x; yRotation = transform.localEulerAngles.y; } void Update () { float x = Input.GetAxis("Horizontal"); float z = Input.GetAxis("Vertical"); Vector3 planeForward = new Vector3(-transform.right.z, 0, transform.right.x); Vector3 move = (transform.right * x + planeForward * z) * speed * Time.deltaTime; transform.Translate(move, Space.World); if (Input.GetMouseButton(1)) { float mouseX = Input.GetAxis("Mouse X") * rotationSpeed * Time.deltaTime; float mouseY = Input.GetAxis("Mouse Y") * rotationSpeed * Time.deltaTime; xRotation -= mouseY; yRotation += mouseX; xRotation = Mathf.Clamp(xRotation, -45f, 45f); transform.localRotation = Quaternion.Euler(xRotation, yRotation, 0f); } } }
Small video of this implementation working
Milestone 3 (23.10)
We want to have some sort of a demo of this project ready as soon as possible. For this reason I want to implement more functionality, that we think should be present in the final product. For milestone 3 I want to:
- Come up with some version of UI design. E.g I need to think about how the menus would be structured, how they should be displayed, which functionality should be present, etc. I want to implement some version of the UI. I will not implement the functionality of all the menu possibilities I can come up with. I will first focus on one thing - a menu where you can see information about the room (possible inventory, dimensions, capacity, price).
- Create functionality to place inventory in the room. As initial inventory, I have chair(s) in mind. I imagine the use case to be something like: The user will select the menu that displays possible inventory for that room. A chair is seen in that menu. The user can click on that inventory item, select "place", and can then move that item around the room and then place it in some available space.
Results
First version of UI flow diagram
A version of the UI flow diagram can be seen below:
Menu and inventory placement functionality
The need for a quick implementation of some menu and inventory placement functionality arose because a first demo (or "proof of concept") was needed. The demo (with some introduction to the business idea) can be seen in the following video (the demo starts around the 24 second mark):
Build of the demo: https://drive.google.com/file/d/1sWvZHBzR2TfQ32r1MVXEuqGObXpRqhgX/view?usp=sharing
The implementation of this inventory placement is very basic and not scalable. When the "chairs" button is pressed, it instantiates a prefab of the group of chairs at the cursor location. The core of the logic is represented by this code:
public class InventoryManager : MonoBehaviour { public Transform chairPrefab; private Transform clone; public Camera cam; public BoxCollider selectionCollider; public BoxCollider placementCollider; bool inPlacement = false; void Start() { selectionCollider.enabled = true; placementCollider.enabled = false; } void Update() { if (inPlacement) { Vector3 mouse = Input.mousePosition; Ray castPoint = cam.ScreenPointToRay(mouse); RaycastHit hit; if (Physics.Raycast(castPoint, out hit, Mathf.Infinity)) ; { clone.transform.position = hit.point; clone.transform.position = new Vector3(clone.transform.position.x, -3.72f, clone.transform.position.z); } if (Input.GetMouseButton(0)) { EndPlacement(); } } } private void EndPlacement() { inPlacement = false; selectionCollider.enabled = true; placementCollider.enabled = false; clone = null; UIManager.IncreaseSeatCount(35); } public void StartChairPlacement() { inPlacement = true; placementCollider.enabled = true; selectionCollider.enabled = false; Vector3 startPos = new Vector3(0, 0, 0); if (clone == null) { clone = Instantiate(chairPrefab, startPos, Quaternion.Euler(-90, 0, 0)); } } }
As we can see, there is a lot of hard-coded stuff and it is not very flexible. I want to improve (completely overhaul) the inventory system as the next milestone.
Milestone 4 (6.11)
As a part of this milestone I want to:
- Make the inventory placement room specific. By this I mean that I want the program to understand which room is selected and that it would be possible to place things only in that room.
- Make the program remember a state of each room. In this case I only have the conference room, but the logic could be applied to other rooms in the end as well.
The next two points are not the main goal of this milestone, but if I have time I will work on these along side the ones mentioned above:
- Enable saving the venue's configuration into a file (json maybe?) and also loading a configuration from a file
- Enable the deletion of placed inventory
Results
Room specific inventory
Each interactable room is a GameObject with a "Room" script. Among other things, the room script holds references to all possible inventory and placed inventory for that room. Each room has a child object that holds a collider for detecting raycasts when placing inventory in that room. UI will display the inventory items relevant for a selected room. The main flow looks like this:
An inventory item can only be placed in the selected room (due to checking raycasts against only the room's placement collider). Upon placement, the "Room" GameObject receives the instantiated inventory item as a child object.
An inventory item is just a prefab with an image (Sprite) and an "InventoryItem" script attached to it. Each prefab has a unique ID (the name of the item).
Remembering room states
For now, the state of a room is just the inventory placed in it. So during runtime, the state of each room is saved anyway. I implemented a saving function for saving the IDs and position of inventory items per room and loading the item back later (following this guide: https://www.raywenderlich.com/418-how-to-save-and-load-a-game-in-unity). My current implementation of this can be improved, but it works for now.
The save class storing the information:
[System.Serializable] public class Save { public List<string> roomNames = new List<string>(); public List<string> inventoryItems = new List<string>(); public List<SerializableVector3> inventoryItemLocations = new List<SerializableVector3>(); public void PutInventory(string roomName, List<InventoryItem> items) { foreach(InventoryItem item in items) { roomNames.Add(roomName); inventoryItems.Add(item.id); inventoryItemLocations.Add(new SerializableVector3(item.transform.position)); } } }
Saving:
Save save = roomManager.CreateInventorySave(); BinaryFormatter bf = new BinaryFormatter(); FileStream file = File.Create(Application.persistentDataPath + "/gamesave.save"); bf.Serialize(file, save); file.Close();
Loading:
BinaryFormatter bf = new BinaryFormatter(); FileStream file = File.Open(Application.persistentDataPath + "/gamesave.save", FileMode.Open); Save save = (Save)bf.Deserialize(file); file.Close(); roomManager.LoadInvetorySave(save);
Reinstantiating the itmes is not very efficient right now. The system can be broken if you spam save / load too fast. Some mechanism should be added, which disables all other actions while saving and loading.
Milestone 5 (20.11)
As a part of this milestone I want to:
- Implement some core features of the "game" logic. The features would be:
- Displaying a main menu when the program is started. As the first functions of the main menu I will add "load layout", "create new layout", "planning mode", "exit". Exploration mode will be exluded for now
- Implement some new core functions of the "planning mode" and concentrate existing ones. These will include room selection from menu (functionality already existing in some form), the room selection / inventory placement (existing in some form), checking availability (new one; some sort of calendar), saving venue layout (improve this function).
Results
Link to build for this milestone: https://drive.google.com/file/d/1MhTrmMQkxoHcmqfyB8XTiuuiE4PDqZXB/view?usp=sharing
Main menu
The "game" now includes a main menu. It is the first thing seen when to user starts the program. The user can also show or hide the main menu by pressing the escape key. Toggling the main menu by escape key is only possible when the user has entered the planning mode at least once (equivalent to "user has started the game").
Main menu has the following functions:
- Continue planning. This is meant to be used when the user is in the middle of planning their event, has opened the main menu, and wants to continue planning their layout. When the layout is empty, this has the same effect as "New layout". Pressing "Continue planning" will take the user back to planning mode and hide the main menu.
- New layout. This option will clear inventory from all the rooms and take the user to planning mode. This can be used when the user wants to reset everything.
- Load layout. This button will open up a list of preset saves of layouts. If a user save is present, it will display that too. Clicking on the name of a save will load the layout stored in that save. Currently, this button does not exit the main menu, but it would be nice if it did. Right now this is just an oversight, and will be changed in the near future.
- Exit. This closes the application.
Planning mode
Planning mode is entered when the user presses either "Continue planning" or "New layout" from the main menu. Right now, planning mode pretty much means that the user is not in the main menu. The user can toggle between the main menu and planning mode as described above in the "Main menu" section.
Closing the main menu and entering planning mode will display the user a "toolbar". It has the following functions:
- Select room. Pressing this will display the user a list of available rooms. When a room in the list is clicked, the room gets selected and the camera is move to show that room. Room selection is still possible by the regular way of clicking on the room in the model.
- Show inventory. This option is only shown when a room is selected. Pressing the "show inventory" button will display the list of items available for the selected room. Selecting an item from the list starts the placement of the item into the room. A little feature added to placing inventory: pressing the "R" key when an inventory item is selected will rotate the item 90 degrees clockwise.
- Show availability. Only available when a room is selected. Right now, this just displays an image of a calendar month - November 2020 :). Its intended purpose is to show the dates for which the room can be booked. But this will be implemented in the future.
- Save layout. Pressing this will create a save of the items currently placed in rooms. This is the "user save". This button will always overwrite the previous user save.
- Load layout. This option is present only if there is a user save present. In contrast to the "Load layout" option of the main menu, clicking this button will only load the user save. It won't even display any list of saves. It will load just the one and only user save.
A thing to improve with the UI would be to add some pop-up messages that inform the user about an action. For example, when pressing the "New layout" button from the main menu, the system could ask if the user is sure they want to erase everything. Or if "save layout" is pressed, it could just say "Layout saved".
An example scenario for a user to test the program could be as follows:
They start the application. When the main menu is shown, they press "New layout" (Continue planning works as well). They will be see the toolbar on the left side of the screen. They click the "Select room" button and click the "Conference room" button (currently the only one present). This will take the camera to the conference room and also enable the actions "Show inventory" and "Show availability". They could check the availability to see the nice image of a calendar page :). Press it again to hide it. They could then check the inventory, click on some item from the list and place it into the room. Do this as many times as they wish. When done, they press the "Save layout" to create a user save. Then press the escape key and click "New layout". This will erase everything they placed before. Back in planning mode, they can press "Load layout" to load the save they created just before. They could enter the main menu again (esc) and select "Load layout" from the main menu. This should show them (as of this milestone) two preset layouts + their user save. Click on any of those load one of the layouts. The user save should persist when they have closed the application and started it again.
Milestone 6 (04.12)
The main goal of this milestone is to add the rest of the building back around the conference room. Additionally, add one (maybe two) other rooms that actually exist at Tondiraba for the user to interact with.
Side goals, that I would like to accomplish if I have the time:
- Make the UI prettier, add some pop-up messages.
- Enable the colliders for inventory items, so they could not be placed inside each other. The colliders will also enable the user to select already placed inventory and move them around (or delete them).
Results
Link to build: https://drive.google.com/file/d/1D2eTx9XnFqe4ILoBTfqCboeXP395nHD3/view
Adding back rest of the build
The basement floor around the main arena, along with the outer walls of the building are back in the model. Also, one other interactable room (choreography room) was added (I did not have a reference image for it, I just guessed what it might look like).
A little extra feature is that when the camera moves inside the building, the outer walls disappear, and vice versa.
From the side goals, I added confirmation messages, when the user tries to do the following actions: start a new layout, load a layout, save a layout, exit the game. The messages also appear when there is actually no danger over overwriting anything. E.g the user tries to load a layout, there is nothing placed in the current layout, but they still get a warning message asking if they want to load. That is a slightly annoying side effect.
Inventory items in the room are now editable after they have been placed. They can be selected to move them around. They can be deleted by selecting the item and pressing the delete key. If an item has been picked up and moved (but not placed down again), the user can cancel the movement by pressing the escape key. Also, items can be rotated by holding down the "R" key and scrolling the mouse wheel. Items will also turn red when they collide with other items, making them unable to be placed inside other items.