- Home /
How can I create an atmosphere/oxygen system in my 3d space game.
Hi,
I'm currently working on a space game in unity, the game is a mix of ftl and pulsar. I want to have oxygen systems that play a role in gameplay for each room, I am still trying to decide on if the ships are random shapes and sizes or created on a tile-based grid. each ship will have multiple rooms all with their own air supply, this supply increases with an oxygen generator and decreases if there is a crew member or fire etc in the room.
The issue I'm having is that I am struggling to think of how to make a system (almost like space engineers and ftl) where the rooms know their pressure, fill with oxygen and if doors are open it will move around freely between them as well as draining out if it is open to the vacuum of space. I know this isn't the best-worded question so any queries about what I mean or the system I'm looking for let me know.
Thank you for your help.
Answer by andrew-lukasik · May 04, 2021 at 08:40 PM
You can make this work as trigger volumes with dedicated component to represent sealed spaces. And script those changes in the environment (doors opening, fires etc) as changes to the oxygenLevel
for given volumes.
Be pragmatic and make some gameplay first. Don't make this a voxelized fluid (gas) simulation project.
AtmosphericVolume.cs
using UnityEngine;
[RequireComponent( typeof(BoxCollider) )]
public class AtmosphericVolume : MonoBehaviour
{
[Range(0,1)] public float oxygenLevel = 1;
#if UNITY_EDITOR
void OnValidate ()
{
var collider = GetComponent<BoxCollider>();
if( !collider.isTrigger )
Debug.LogWarning("make me a trigger human!", gameObject);
}
void OnDrawGizmos ()
{
var collider = GetComponent<BoxCollider>();
if( collider!=null )
{
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.color = oxygenLevel<1 ? Color.black : Color.white;
Gizmos.DrawWireCube( collider.center , collider.size );
Gizmos.color = new Color{ r=1f , g=1f , b=1f , a=0.05f };
Gizmos.DrawCube( collider.center , collider.size );
}
}
#endif
}
BreathingPawn.cs
using System.Collections.Generic;
using UnityEngine;
[RequireComponent( typeof(CapsuleCollider) )]
[RequireComponent( typeof(Rigidbody) )]
public class BreathingPawn : MonoBehaviour
{
// note: not a List but HashSet because it prevents duplication
HashSet<AtmosphericVolume> _oxygenSources = new HashSet<AtmosphericVolume>();
[SerializeField][Range(0,1)] float _oxygen = 1f;
[SerializeField][Min(0)] float _oxygenReplenishment = 0.05f;
[SerializeField][Min(0)] float _oxygenConsumption = 0.05f;
[SerializeField] AnimationCurve _oxygenLevelViability = new AnimationCurve( new Keyframe(0,0,0,0) , new Keyframe(1,1,3.3f,0) );
const float k_breathing_tick_rate = 1f / 3f;// 3 times per second
void OnEnable ()
{
// This will create a custom Update-like function that will be called repeatedly.
// I'm not using Update because breathing don't have to be calculated every time a frame is drawn (just wasteful)
InvokeRepeating( nameof(Tick_Breathing) , time:k_breathing_tick_rate , repeatRate:k_breathing_tick_rate );
}
void OnTriggerEnter ( Collider other )
{
var comp = other.GetComponent<AtmosphericVolume>();
if( comp!=null )
_oxygenSources.Add(comp);
}
void OnTriggerExit ( Collider other )
{
var comp = other.GetComponent<AtmosphericVolume>();
if( comp!=null )
_oxygenSources.Remove(comp);
}
#if UNITY_EDITOR
void OnDrawGizmos ()
{
Gizmos.color = Color.white;
Vector3 pos = transform.position;
foreach( var source in _oxygenSources )
Gizmos.DrawLine( pos , source.transform.position );
}
#endif
void Tick_Breathing ()
{
// in cases when atmo volumes overlap - pick the better one:
float oxygenAround = 0f;
foreach( var volume in _oxygenSources )
oxygenAround = Mathf.Max( oxygenAround , volume.oxygenLevel );
// breathing:
float oxygenIntake = _oxygenReplenishment * k_breathing_tick_rate * _oxygenLevelViability.Evaluate(oxygenAround);
_oxygen = Mathf.Clamp01( _oxygen + oxygenIntake );
// oxygen consumption:
float oxygenConsumed = _oxygenConsumption * k_breathing_tick_rate * ( 1f - oxygenAround );
_oxygen = Mathf.Clamp01( _oxygen - oxygenConsumed );
if( !(_oxygen>0) )
OnAsphyxiation();
}
void OnAsphyxiation ()
{
Debug.Log($"{gameObject.name} is x__x (asphyxiation)",gameObject);
gameObject.SetActive( false );
}
}
This is actually a really smart way of doing it, thank you, I'll try implementing it and let you know how it goes.
Your answer
Follow this Question
Related Questions
Space Shooter Player 2 ship is flipping when going into Play Mode 0 Answers
Simulating Rocket stage separation. 0 Answers
First Person Camera switch to Third Person Camera on Object 2 Answers
Strategies for Physics-based Movement for 2D Space RTS Controls? 1 Answer
My 2d tractor beam is not working 1 Answer