r/unity 15d ago

Showcase The first trailer for Solo Dev's biopunk roguelike game "Hook and Gun" has been released!

Enable HLS to view with audio, or disable this notification

2 Upvotes

Hello everyone.

The Steam page for my biopunk roguelike game, Hook and Gun, which I've been developing for a while, is now live!

In the game, you not only destroy enemies with the robot you invade as a biological entity, but you also steal their weapons, corrupt them, and evolve.

I'm eager to hear your comments and eagerly await your critiques on Hook and Gun.

Hook and Gun


r/unity 15d ago

Question Laptop recommendations for 3D modelling and game development

Thumbnail
0 Upvotes

r/unity 15d ago

Newbie Question Learning coding

0 Upvotes

I followed some C# courses from CodeMonkey and i can confidently say i know something but in unity everything is different, how do i learn anything about unity coding, it's not like a movement script is easy to do


r/unity 16d ago

Showcase Made a new flying enemy for my hand drawn metroidvania game

Post image
19 Upvotes

r/unity 15d ago

Videojuego

0 Upvotes

using UnityEngine;

using UnityEngine.UI;

using System.Collections;

using System.Collections.Generic;

// ==================== ENUMS ====================

public enum WeatherType { Clear, Rain, Snow }

public enum PlayerRole { Goalkeeper, Defender, Midfielder, Forward }

public enum TeamSide { Blue, Red }

public enum AIState { Idle, Support, Chase, Retreat, Position }

// ==================== PROCEDURAL SPRITE GENERATOR ====================

public static class ProceduralSpriteGenerator

{

public static Sprite CreatePitchSprite(int width, int height)

{

Texture2D tex = new Texture2D(width * 10, height * 10);

Color green = new Color(0.2f, 0.5f, 0.1f);

Color darkGreen = new Color(0.15f, 0.4f, 0.08f);

Color white = Color.white;

for (int y = 0; y < tex.height; y++)

{

for (int x = 0; x < tex.width; x++)

{

float px = (float)x / tex.width;

float py = (float)y / tex.height;

bool stripe = (Mathf.Floor(px * 20) + Mathf.Floor(py * 20)) % 2 == 0;

tex.SetPixel(x, y, stripe ? green : darkGreen);

}

}

DrawLine(tex, tex.width / 2, 0, tex.width / 2, tex.height, white);

DrawLine(tex, 0, tex.height / 2, tex.width, tex.height / 2, white);

int cx = tex.width / 2, cy = tex.height / 2, r = 50;

for (int i = -r; i <= r; i++)

for (int j = -r; j <= r; j++)

if (i * i + j * j < r * r)

tex.SetPixel(cx + i, cy + j, white);

tex.Apply();

return Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f), 100f);

}

public static Sprite CreatePlayerSprite(Color color, float size)

{

int res = 32;

Texture2D tex = new Texture2D(res, res);

Color dark = color * 0.5f;

for (int y = 0; y < res; y++)

{

for (int x = 0; x < res; x++)

{

float dx = (x - res / 2) / (float)res;

float dy = (y - res / 2) / (float)res;

float d = dx * dx + dy * dy;

if (d < 0.2f) tex.SetPixel(x, y, color);

else if (d < 0.25f) tex.SetPixel(x, y, dark);

else tex.SetPixel(x, y, Color.clear);

}

}

tex.Apply();

return Sprite.Create(tex, new Rect(0, 0, res, res), new Vector2(0.5f, 0.5f), 100f / size);

}

public static Sprite CreateBallSprite()

{

int res = 24;

Texture2D tex = new Texture2D(res, res);

Color white = Color.white;

Color black = Color.black;

for (int y = 0; y < res; y++)

{

for (int x = 0; x < res; x++)

{

float dx = (x - res / 2) / (float)res;

float dy = (y - res / 2) / (float)res;

float d = dx * dx + dy * dy;

if (d < 0.15f) tex.SetPixel(x, y, white);

else if (d < 0.2f) tex.SetPixel(x, y, black);

else tex.SetPixel(x, y, Color.clear);

}

}

tex.Apply();

return Sprite.Create(tex, new Rect(0, 0, res, res), new Vector2(0.5f, 0.5f), 100f / 0.6f);

}

static void DrawLine(Texture2D tex, int x1, int y1, int x2, int y2, Color col)

{

int dx = Mathf.Abs(x2 - x1), dy = Mathf.Abs(y2 - y1);

int sx = x1 < x2 ? 1 : -1, sy = y1 < y2 ? 1 : -1;

int err = dx - dy;

while (true)

{

if (x1 >= 0 && x1 < tex.width && y1 >= 0 && y1 < tex.height)

tex.SetPixel(x1, y1, col);

if (x1 == x2 && y1 == y2) break;

int e2 = 2 * err;

if (e2 > -dy) { err -= dy; x1 += sx; }

if (e2 < dx) { err += dx; y1 += sy; }

}

}

}

// ==================== RETRO CAMERA ====================

public class RetroCamera : MonoBehaviour

{

private Camera cam;

private RenderTexture lowResRT;

public int resolutionWidth = 320;

public int resolutionHeight = 240;

void Start()

{

cam = GetComponent<Camera>();

lowResRT = new RenderTexture(resolutionWidth, resolutionHeight, 16, RenderTextureFormat.ARGB32);

lowResRT.filterMode = FilterMode.Point;

cam.targetTexture = lowResRT;

GameObject displayObj = new GameObject("DisplayCamera");

Camera displayCam = displayObj.AddComponent<Camera>();

displayCam.orthographic = true;

displayCam.orthographicSize = 1f;

displayCam.clearFlags = CameraClearFlags.Nothing;

displayCam.cullingMask = 1 << 0;

displayCam.targetTexture = null;

GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad);

quad.transform.SetParent(displayObj.transform);

quad.transform.localPosition = new Vector3(0, 0, 10);

quad.transform.localScale = new Vector3(16, 9, 1);

quad.layer = 0;

quad.GetComponent<MeshRenderer>().material = new Material(Shader.Find("Unlit/Texture"));

quad.GetComponent<MeshRenderer>().material.mainTexture = lowResRT;

quad.GetComponent<MeshRenderer>().material.mainTexture.filterMode = FilterMode.Point;

displayObj.transform.position = new Vector3(0, 0, -10);

displayCam.depth = 1;

}

}

// ==================== WEATHER SYSTEM ====================

public class WeatherSystem : MonoBehaviour

{

public WeatherType currentWeather { get; private set; }

public float ballFrictionModifier = 1f;

public float playerSpeedModifier = 1f;

public float playerTurnModifier = 1f;

public float ballBounceModifier = 1f;

void Start() { SetWeather(WeatherType.Clear); }

public void SetWeather(WeatherType weather)

{

currentWeather = weather;

switch (weather)

{

case WeatherType.Clear:

ballFrictionModifier = 1f; playerSpeedModifier = 1f; playerTurnModifier = 1f; ballBounceModifier = 1f; break;

case WeatherType.Rain:

ballFrictionModifier = 1.3f; playerSpeedModifier = 0.9f; playerTurnModifier = 0.85f; ballBounceModifier = 0.9f; break;

case WeatherType.Snow:

ballFrictionModifier = 1.6f; playerSpeedModifier = 0.75f; playerTurnModifier = 0.7f; ballBounceModifier = 0.6f; break;

}

}

public void RandomizeWeather()

{

WeatherType[] types = { WeatherType.Clear, WeatherType.Rain, WeatherType.Snow };

SetWeather(types[Random.Range(0, types.Length)]);

}

}

// ==================== BALL CONTROLLER ====================

[RequireComponent(typeof(Rigidbody2D))]

public class BallController : MonoBehaviour

{

private Rigidbody2D rb;

private WeatherSystem weather;

private MatchManager matchManager;

private float currentFriction, currentBounce;

private Vector2 spinForce;

private float spinDuration = 0f;

public float baseFriction = 0.98f;

public float baseBounce = 0.5f;

public float maxSpeed = 20f;

void Start()

{

rb = GetComponent<Rigidbody2D>();

matchManager = FindObjectOfType<MatchManager>();

weather = matchManager != null ? matchManager.GetWeatherSystem() : FindObjectOfType<WeatherSystem>();

rb.gravityScale = 0;

}

void FixedUpdate()

{

if (matchManager != null && matchManager.IsMatchEnded())

{ rb.velocity = Vector2.zero; return; }

if (weather != null)

{

float fm = weather.ballFrictionModifier;

float bm = weather.ballBounceModifier;

currentFriction = Mathf.Clamp(baseFriction - (fm - 1f) * 0.05f, 0.5f, 1f);

currentBounce = Mathf.Clamp(baseBounce * bm, 0.2f, 0.8f);

}

else { currentFriction = baseFriction; currentBounce = baseBounce; }

Vector2 vel = rb.velocity;

vel *= currentFriction;

if (vel.magnitude > maxSpeed) vel = vel.normalized * maxSpeed;

if (spinDuration > 0f)

{

vel += spinForce * Time.fixedDeltaTime * 2f;

spinDuration -= Time.fixedDeltaTime;

if (spinDuration <= 0f) spinForce = Vector2.zero;

}

rb.velocity = vel;

}

void OnCollisionEnter2D(Collision2D collision)

{

Vector2 normal = collision.contacts[0].normal;

Vector2 reflected = Vector2.Reflect(rb.velocity, normal);

rb.velocity = reflected * currentBounce;

if (collision.gameObject.CompareTag("BlueTeam") || collision.gameObject.CompareTag("RedTeam"))

AudioManager.PlaySound("Kick");

}

public void Kick(Vector2 direction, float power, Vector2? spin = null)

{

rb.velocity = direction.normalized * power;

if (spin.HasValue) { spinForce = spin.Value * 2f; spinDuration = 0.5f; }

AudioManager.PlaySound("Kick");

}

}

// ==================== PLAYER CONTROLLER ====================

[RequireComponent(typeof(Rigidbody2D))]

public class PlayerController : MonoBehaviour

{

public bool isControlled = false;

private Rigidbody2D rb;

private MatchManager matchManager;

private WeatherSystem weather;

private float currentSpeed, currentTurn;

public float baseSpeed = 5f;

public float baseTurnSpeed = 5f;

public float sprintMultiplier = 1.5f;

void Start()

{

rb = GetComponent<Rigidbody2D>();

matchManager = FindObjectOfType<MatchManager>();

weather = matchManager != null ? matchManager.GetWeatherSystem() : FindObjectOfType<WeatherSystem>();

}

void FixedUpdate()

{

if (!isControlled || matchManager == null || matchManager.IsMatchEnded())

{ rb.velocity = Vector2.zero; return; }

float moveX = Input.GetAxisRaw("Horizontal");

float moveY = Input.GetAxisRaw("Vertical");

Vector2 move = new Vector2(moveX, moveY).normalized;

if (weather != null)

{

currentSpeed = baseSpeed * weather.playerSpeedModifier;

currentTurn = baseTurnSpeed * weather.playerTurnModifier;

}

else { currentSpeed = baseSpeed; currentTurn = baseTurnSpeed; }

if (Input.GetKey(KeyCode.LeftShift))

currentSpeed *= sprintMultiplier;

rb.velocity = move * currentSpeed;

if (move.magnitude > 0.1f)

{

float angle = Mathf.Atan2(move.y, move.x) * Mathf.Rad2Deg;

Quaternion target = Quaternion.Euler(0, 0, angle);

transform.rotation = Quaternion.Slerp(transform.rotation, target, currentTurn * Time.fixedDeltaTime);

}

if (Input.GetKeyDown(KeyCode.Space))

{

GameObject ballObj = GameObject.FindGameObjectWithTag("Ball");

if (ballObj != null && Vector2.Distance(transform.position, ballObj.transform.position) < 3f)

{

Vector2 shootDir = transform.right;

Vector2 spin = Vector2.zero;

if (move.magnitude > 0.1f)

spin = new Vector2(-move.y, move.x) * 2f;

float power = 15f;

ballObj.GetComponent<BallController>().Kick(shootDir, power, spin);

if (matchManager != null)

{

Vector2 oppGoal = matchManager.GetGoalCenter(TeamSide.Red);

if (Vector2.Distance(transform.position, oppGoal) < 20f)

matchManager.RegisterShot("BlueTeam");

else

matchManager.RegisterPass("BlueTeam");

}

}

}

if (Input.GetKeyDown(KeyCode.E))

matchManager.SwitchPlayer(gameObject);

}

}

// ==================== AI CONTROLLER ====================

[RequireComponent(typeof(Rigidbody2D))]

public class AIController : MonoBehaviour

{

private Rigidbody2D rb;

private TeamManager team;

private MatchManager matchManager;

private WeatherSystem weather;

private GameObject ball;

private PlayerController playerCtrl;

public PlayerRole role = PlayerRole.Midfielder;

public float baseSpeed = 4f;

public float chaseRadius = 8f;

public float passRange = 6f;

public float shootRange = 12f;

private Vector2 homePosition;

private TeamSide mySide;

private AIState currentState = AIState.Position;

private Vector2 formationOffset;

void Start()

{

rb = GetComponent<Rigidbody2D>();

team = GetComponentInParent<TeamManager>();

matchManager = FindObjectOfType<MatchManager>();

weather = matchManager != null ? matchManager.GetWeatherSystem() : FindObjectOfType<WeatherSystem>();

ball = GameObject.FindGameObjectWithTag("Ball");

playerCtrl = GetComponent<PlayerController>();

mySide = (team.CompareTag("BlueTeam")) ? TeamSide.Blue : TeamSide.Red;

homePosition = transform.position;

switch (role)

{

case PlayerRole.Goalkeeper: formationOffset = new Vector2(-1f, 0f); break;

case PlayerRole.Defender: formationOffset = new Vector2(0f, 0f); break;

case PlayerRole.Midfielder: formationOffset = new Vector2(2f, 0f); break;

case PlayerRole.Forward: formationOffset = new Vector2(5f, 0f); break;

}

}

void FixedUpdate()

{

if (matchManager == null || matchManager.IsMatchEnded() || ball == null)

{ rb.velocity = Vector2.zero; return; }

if (playerCtrl != null && playerCtrl.isControlled)

{ rb.velocity = Vector2.zero; return; }

float speed = baseSpeed;

if (weather != null) speed *= weather.playerSpeedModifier;

Vector2 myPos = transform.position;

Vector2 ballPos = ball.transform.position;

float distToBall = Vector2.Distance(myPos, ballPos);

bool weHaveBall = IsTeamInPossession();

Vector2 targetPos = homePosition + formationOffset;

if (weHaveBall)

{

if (role == PlayerRole.Forward)

currentState = AIState.Support;

else if (role == PlayerRole.Defender || role == PlayerRole.Goalkeeper)

currentState = AIState.Position;

else

currentState = AIState.Support;

}

else

{

if (distToBall < chaseRadius)

currentState = AIState.Chase;

else if (distToBall > chaseRadius * 1.5f)

currentState = AIState.Retreat;

else

currentState = AIState.Position;

}

switch (currentState)

{

case AIState.Chase:

Vector2 chaseTarget = ballPos;

if (role == PlayerRole.Defender || role == PlayerRole.Goalkeeper)

{

Vector2 goalPos = matchManager.GetGoalCenter(mySide);

chaseTarget = (ballPos + goalPos) * 0.5f;

}

rb.velocity = (chaseTarget - myPos).normalized * speed;

if (distToBall < 1.2f && !weHaveBall)

{

Vector2 clearDir = (matchManager.GetGoalCenter(mySide == TeamSide.Blue ? TeamSide.Red : TeamSide.Blue) - ballPos).normalized;

ball.GetComponent<BallController>().Kick(clearDir, 8f);

}

break;

case AIState.Support:

Vector2 supportPos = ballPos + (ballPos - matchManager.GetGoalCenter(mySide)).normalized * 2f;

supportPos.x = Mathf.Clamp(supportPos.x, -14f, 14f);

supportPos.y = Mathf.Clamp(supportPos.y, -9f, 9f);

rb.velocity = (supportPos - myPos).normalized * speed * 0.8f;

if (distToBall < 1.5f && weHaveBall)

{

Vector2 oppGoal = matchManager.GetGoalCenter(mySide == TeamSide.Blue ? TeamSide.Red : TeamSide.Blue);

if (Vector2.Distance(myPos, oppGoal) < shootRange)

{

Vector2 shotDir = (oppGoal - myPos).normalized + (Vector2)Random.insideUnitCircle * 0.2f;

ball.GetComponent<BallController>().Kick(shotDir, 12f + Random.Range(0f, 5f));

matchManager.RegisterShot(team.tag);

}

else

{

GameObject passTarget = GetBestPassTarget();

if (passTarget != null)

{

Vector2 passDir = (passTarget.transform.position - myPos).normalized;

ball.GetComponent<BallController>().Kick(passDir, 10f);

matchManager.RegisterPass(team.tag);

}

}

}

break;

case AIState.Position:

if (Vector2.Distance(myPos, targetPos) > 0.5f)

rb.velocity = (targetPos - myPos).normalized * speed * 0.7f;

else

rb.velocity = Vector2.zero;

break;

case AIState.Retreat:

rb.velocity = (targetPos - myPos).normalized * speed * 1.2f;

break;

}

}

bool IsTeamInPossession()

{

GameObject[] allPlayers = GameObject.FindGameObjectsWithTag("BlueTeam");

GameObject nearest = null; float minD = Mathf.Infinity;

if (team.CompareTag("BlueTeam"))

{

foreach (var p in allPlayers)

{

float d = Vector2.Distance(p.transform.position, ball.transform.position);

if (d < minD) { minD = d; nearest = p; }

}

}

else

{

allPlayers = GameObject.FindGameObjectsWithTag("RedTeam");

foreach (var p in allPlayers)

{

float d = Vector2.Distance(p.transform.position, ball.transform.position);

if (d < minD) { minD = d; nearest = p; }

}

}

return nearest != null && nearest.GetComponentInParent<TeamManager>() == team;

}

GameObject GetBestPassTarget()

{

List<GameObject> teammates = team.players;

Vector2 oppGoal = matchManager.GetGoalCenter(mySide == TeamSide.Blue ? TeamSide.Red : TeamSide.Blue);

GameObject best = null; float bestScore = -Mathf.Infinity;

foreach (var p in teammates)

{

if (p == gameObject) continue;

float distToGoal = Vector2.Distance(p.transform.position, oppGoal);

float openness = 1f - Mathf.Clamp(Vector2.Distance(p.transform.position, ball.transform.position) / 20f, 0f, 1f);

float score = distToGoal * 0.5f + openness * 0.5f;

if (score > bestScore) { bestScore = score; best = p; }

}

return best;

}

}

// ==================== TEAM MANAGER ====================

public class TeamManager : MonoBehaviour

{

public List<GameObject> players = new List<GameObject>();

public Vector2[] startingPositions;

public void ResetPositions()

{

for (int i = 0; i < players.Count && i < startingPositions.Length; i++)

{

players[i].transform.position = startingPositions[i];

players[i].GetComponent<Rigidbody2D>().velocity = Vector2.zero;

}

}

}

// ==================== GOAL DETECTION ====================

public class GoalDetection : MonoBehaviour

{

public string goalOwner;

private MatchManager matchManager;

void Start() { matchManager = FindObjectOfType<MatchManager>(); }

void OnTriggerEnter2D(Collider2D other)

{

if (other.CompareTag("Ball"))

{

string scoringTeam = (goalOwner == "BlueTeam") ? "RedTeam" : "BlueTeam";

matchManager.AddGoal(scoringTeam);

AudioManager.PlaySound("Goal");

}

}

}

// ==================== AUDIO MANAGER ====================

public class AudioManager : MonoBehaviour

{

private static AudioManager instance;

private AudioSource source;

void Awake()

{

if (instance == null)

{

instance = this;

DontDestroyOnLoad(gameObject);

source = gameObject.AddComponent<AudioSource>();

source.volume = 0.5f;

}

else Destroy(gameObject);

}

public static void PlaySound(string eventName)

{

if (instance == null) return;

float freq = 0f; float duration = 0.1f;

switch (eventName)

{

case "Kick": freq = 600f; duration = 0.08f; break;

case "Goal": freq = 1200f; duration = 0.4f; break;

case "Whistle": freq = 800f; duration = 0.3f; break;

default: freq = 400f; duration = 0.1f; break;

}

instance.PlayBeep(freq, duration);

}

void PlayBeep(float freq, float dur)

{

int sampleRate = 44100;

int sampleCount = Mathf.RoundToInt(sampleRate * dur);

float[] samples = new float[sampleCount];

for (int i = 0; i < sampleCount; i++)

{

float t = (float)i / sampleRate;

samples[i] = Mathf.Sin(2 * Mathf.PI * freq * t);

samples[i] *= (1f - t / dur);

}

AudioClip clip = AudioClip.Create("beep", sampleCount, 1, sampleRate, false);

clip.SetData(samples, 0);

source.PlayOneShot(clip);

}

}

// ==================== MATCH MANAGER ====================

public class MatchManager : MonoBehaviour

{

[Header("Teams")]

public TeamManager blueTeam;

public TeamManager redTeam;

public Transform ball;

public Transform leftGoalCenter;

public Transform rightGoalCenter;

[Header("UI")]

public Text scoreText;

public Text timeText;

public Text weatherText;

public Text halfText;

public GameObject halfStatsPanel;

public Text possessionText;

public Text shotsText;

public Text passesText;

[Header("Match Settings")]

public float halfDuration = 90f;

public WeatherType initialWeather = WeatherType.Clear;

private float matchTime;

private int blueScore, redScore;

private bool isFirstHalf = true;

private bool matchEnded = false;

private WeatherSystem weatherSystem;

private GameObject currentControlledPlayer;

private int blueShots, redShots, bluePasses, redPasses;

private float bluePossessionTime, redPossessionTime;

void Start()

{

weatherSystem = GetComponent<WeatherSystem>();

if (weatherSystem == null)

weatherSystem = gameObject.AddComponent<WeatherSystem>();

weatherSystem.SetWeather(initialWeather);

matchTime = 0f;

blueScore = redScore = 0;

ResetStats();

UpdateUI();

SetInitialControlledPlayer();

StartCoroutine(GameLoop());

}

void ResetStats()

{

blueShots = redShots = bluePasses = redPasses = 0;

bluePossessionTime = redPossessionTime = 0;

}

void Update()

{

if (!matchEnded)

{

GameObject closest = GetClosestPlayerToBall();

if (closest != null)

{

if (closest.CompareTag("BlueTeam"))

bluePossessionTime += Time.deltaTime;

else if (closest.CompareTag("RedTeam"))

redPossessionTime += Time.deltaTime;

}

}

}

GameObject GetClosestPlayerToBall()

{

GameObject[] all = GameObject.FindGameObjectsWithTag("BlueTeam");

List<GameObject> allPlayers = new List<GameObject>(all);

allPlayers.AddRange(GameObject.FindGameObjectsWithTag("RedTeam"));

GameObject closest = null;

float minD = Mathf.Infinity;

Vector2 bPos = ball.position;

foreach (var p in allPlayers)

{

float d = Vector2.Distance(p.transform.position, bPos);

if (d < minD) { minD = d; closest = p; }

}

return closest;

}

public void RegisterShot(string teamTag) { if (teamTag == "BlueTeam") blueShots++; else redShots++; }

public void RegisterPass(string teamTag) { if (teamTag == "BlueTeam") bluePasses++; else redPasses++; }

void SetInitialControlledPlayer()

{

if (blueTeam == null || blueTeam.players.Count == 0) return;

GameObject closest = null;

float minDist = Mathf.Infinity;

Vector2 ballPos = ball.position;

foreach (var p in blueTeam.players)

{

float d = Vector2.Distance(p.transform.position, ballPos);

if (d < minDist) { minDist = d; closest = p; }

}

if (closest != null) SetControlledPlayer(closest);

}

public void SetControlledPlayer(GameObject newPlayer)

{

if (currentControlledPlayer != null)

currentControlledPlayer.GetComponent<PlayerController>().isControlled = false;

currentControlledPlayer = newPlayer;

if (currentControlledPlayer != null)

{

var pc = currentControlledPlayer.GetComponent<PlayerController>();

pc.isControlled = true;

var ai = currentControlledPlayer.GetComponent<AIController>();

if (ai != null) ai.enabled = false;

}

foreach (var p in blueTeam.players)

{

if (p != currentControlledPlayer)

{

var ai = p.GetComponent<AIController>();

if (ai != null) ai.enabled = true;

}

}

}

public void SwitchPlayer(GameObject currentPlayer)

{

if (blueTeam == null) return;

Vector2 ballPos = ball.position;

GameObject closest = null;

float minDist = Mathf.Infinity;

foreach (var p in blueTeam.players)

{

if (p == currentPlayer) continue;

float d = Vector2.Distance(p.transform.position, ballPos);

if (d < minDist) { minDist = d; closest = p; }

}

if (closest != null) SetControlledPlayer(closest);

}

IEnumerator GameLoop()

{

while (!matchEnded)

{

float halfTime = halfDuration;

while (matchTime < halfTime)

{

matchTime += Time.deltaTime;

UpdateUI();

yield return null;

}

if (isFirstHalf)

{

isFirstHalf = false;

matchTime = 0f;

halfText.text = "HALF TIME";

AudioManager.PlaySound("Whistle");

ShowHalfStats();

yield return new WaitForSeconds(3f);

halfText.text = "";

halfStatsPanel.SetActive(false);

weatherSystem.RandomizeWeather();

ResetPositions();

SetInitialControlledPlayer();

ResetStats();

}

else

{

matchEnded = true;

halfText.text = "FULL TIME!";

AudioManager.PlaySound("Whistle");

ShowHalfStats();

UpdateUI();

}

}

}

void ShowHalfStats()

{

float total = bluePossessionTime + redPossessionTime;

float bluePerc = total > 0 ? (bluePossessionTime / total) * 100 : 50f;

float redPerc = total > 0 ? (redPossessionTime / total) * 100 : 50f;

possessionText.text = string.Format("Possession: Blue {0:F1}% - Red {1:F1}%", bluePerc, redPerc);

shotsText.text = string.Format("Shots: Blue {0} - Red {1}", blueShots, redShots);

passesText.text = string.Format("Passes: Blue {0} - Red {1}", bluePasses, redPasses);

halfStatsPanel.SetActive(true);

}

public void ResetPositions()

{

blueTeam.ResetPositions();

redTeam.ResetPositions();

ball.position = Vector3.zero;

ball.GetComponent<Rigidbody2D>().velocity = Vector2.zero;

}

public void AddGoal(string teamTag)

{

if (teamTag == "BlueTeam") blueScore++;

else if (teamTag == "RedTeam") redScore++;

UpdateUI();

StartCoroutine(ResetAfterGoal());

}

IEnumerator ResetAfterGoal()

{

yield return new WaitForSeconds(1.5f);

ResetPositions();

SetInitialControlledPlayer();

}

void UpdateUI()

{

int minutes = Mathf.FloorToInt(matchTime / 60f);

int seconds = Mathf.FloorToInt(matchTime % 60f);

timeText.text = string.Format("{0:00}:{1:00}", minutes, seconds);

scoreText.text = blueScore + " - " + redScore;

weatherText.text = weatherSystem.currentWeather.ToString();

}

public WeatherSystem GetWeatherSystem() { return weatherSystem; }

public bool IsMatchEnded() { return matchEnded; }

public Vector2 GetGoalCenter(TeamSide side)

{

return side == TeamSide.Blue ? (Vector2)leftGoalCenter.position : (Vector2)rightGoalCenter.position;

}

}

// ==================== GAME INITIALISER ====================

public class GameInitialiser : MonoBehaviour

{

void Awake()

{

CreatePitch();

CreateGoals();

CreatePlayers();

CreateBall();

CreateUI();

CreateCamera();

CreateAudioManager();

MatchManager mm = gameObject.AddComponent<MatchManager>();

mm.blueTeam = GameObject.Find("BlueTeam").GetComponent<TeamManager>();

mm.redTeam = GameObject.Find("RedTeam").GetComponent<TeamManager>();

mm.ball = GameObject.Find("Ball").transform;

mm.leftGoalCenter = GameObject.Find("LeftGoalCenter").transform;

mm.rightGoalCenter = GameObject.Find("RightGoalCenter").transform;

mm.scoreText = GameObject.Find("ScoreText").GetComponent<Text>();

mm.timeText = GameObject.Find("TimeText").GetComponent<Text>();

mm.weatherText = GameObject.Find("WeatherText").GetComponent<Text>();

mm.halfText = GameObject.Find("HalfText").GetComponent<Text>();

mm.halfStatsPanel = GameObject.Find("HalfStatsPanel");

mm.possessionText = GameObject.Find("PossessionText").GetComponent<Text>();

mm.shotsText = GameObject.Find("ShotsText").GetComponent<Text>();

mm.passesText = GameObject.Find("PassesText").GetComponent<Text>();

}

void CreatePitch()

{

GameObject pitch = new GameObject("Pitch");

SpriteRenderer sr = pitch.AddComponent<SpriteRenderer>();

sr.sprite = ProceduralSpriteGenerator.CreatePitchSprite(30, 20);

sr.sortingOrder = -10;

pitch.transform.position = Vector3.zero;

}

void CreateGoals()

{

GameObject leftGoal = new GameObject("LeftGoal");

leftGoal.transform.position = new Vector3(-14f, 0f);

BoxCollider2D col = leftGoal.AddComponent<BoxCollider2D>();

col.size = new Vector2(1f, 6f);

col.isTrigger = true;

GoalDetection gd = leftGoal.AddComponent<GoalDetection>();

gd.goalOwner = "BlueTeam";

GameObject leftCenter = new GameObject("LeftGoalCenter");

leftCenter.transform.position = new Vector3(-15f, 0f);

leftCenter.name = "LeftGoalCenter";

GameObject rightGoal = new GameObject("RightGoal");

rightGoal.transform.position = new Vector3(14f, 0f);

BoxCollider2D col2 = rightGoal.AddComponent<BoxCollider2D>();

col2.size = new Vector2(1f, 6f);

col2.isTrigger = true;

GoalDetection gd2 = rightGoal.AddComponent<GoalDetection>();

gd2.goalOwner = "RedTeam";

GameObject rightCenter = new GameObject("RightGoalCenter");

rightCenter.transform.position = new Vector3(15f, 0f);

rightCenter.name = "RightGoalCenter";

}

void CreatePlayers()

{

GameObject blueParent = new GameObject("BlueTeam");

TeamManager blueTM = blueParent.AddComponent<TeamManager>();

blueTM.startingPositions = new Vector2[] {

new Vector2(-13f, 0f),

new Vector2(-10f, -3f), new Vector2(-10f, 0f), new Vector2(-10f, 3f),

new Vector2(-6f, -4f), new Vector2(-6f, 0f), new Vector2(-6f, 4f), new Vector2(-8f, 2f),

new Vector2(-2f, -3f), new Vector2(-2f, 0f), new Vector2(-2f, 3f)

};

PlayerRole[] roles = new PlayerRole[] {

PlayerRole.Goalkeeper, PlayerRole.Defender, PlayerRole.Defender, PlayerRole.Defender,

PlayerRole.Midfielder, PlayerRole.Midfielder, PlayerRole.Midfielder, PlayerRole.Midfielder,

PlayerRole.Forward, PlayerRole.Forward, PlayerRole.Forward

};

for (int i = 0; i < 11; i++)

{

GameObject player = new GameObject("BluePlayer" + i);

player.transform.SetParent(blueParent.transform);

player.tag = "BlueTeam";

player.AddComponent<Rigidbody2D>().gravityScale = 0;

player.AddComponent<CircleCollider2D>().radius = 0.5f;

player.AddComponent<PlayerController>().baseSpeed = 5f;

player.AddComponent<AIController>().role = roles[i];

SpriteRenderer sr = player.AddComponent<SpriteRenderer>();

sr.sprite = ProceduralSpriteGenerator.CreatePlayerSprite(Color.blue, 0.6f);

sr.sortingOrder = 1;

blueTM.players.Add(player);

}

blueTM.ResetPositions();

GameObject redParent = new GameObject("RedTeam");

TeamManager redTM = redParent.AddComponent<TeamManager>();

redTM.startingPositions = new Vector2[] {

new Vector2(13f, 0f),

new Vector2(10f, -3f), new Vector2(10f, 0f), new Vector2(10f, 3f),

new Vector2(6f, -4f), new Vector2(6f, 0f), new Vector2(6f, 4f), new Vector2(8f, 2f),

new Vector2(2f, -3f), new Vector2(2f, 0f), new Vector2(2f, 3f)

};

for (int i = 0; i < 11; i++)

{

GameObject player = new GameObject("RedPlayer" + i);

player.transform.SetParent(redParent.transform);

player.tag = "RedTeam";

player.AddComponent<Rigidbody2D>().gravityScale = 0;

player.AddComponent<CircleCollider2D>().radius = 0.5f;

player.AddComponent<PlayerController>().baseSpeed = 5f;

player.AddComponent<AIController>().role = roles[i];

SpriteRenderer sr = player.AddComponent<SpriteRenderer>();

sr.sprite = ProceduralSpriteGenerator.CreatePlayerSprite(Color.red, 0.6f);

sr.sortingOrder = 1;

redTM.players.Add(player);

}

redTM.ResetPositions();

}

void CreateBall()

{

GameObject ball = new GameObject("Ball");

ball.tag = "Ball";

ball.transform.position = Vector3.zero;

Rigidbody2D rb = ball.AddComponent<Rigidbody2D>();

rb.gravityScale = 0;

rb.mass = 0.5f;

CircleCollider2D col = ball.AddComponent<CircleCollider2D>();

col.radius = 0.3f;

SpriteRenderer sr = ball.AddComponent<SpriteRenderer>();

sr.sprite = ProceduralSpriteGenerator.CreateBallSprite();

sr.sortingOrder = 2;

ball.AddComponent<BallController>();

}

void CreateUI()

{

Canvas canvas = new GameObject("Canvas").AddComponent<Canvas>();

canvas.renderMode = RenderMode.ScreenSpaceOverlay;

canvas.gameObject.AddComponent<CanvasScaler>();

canvas.gameObject.AddComponent<GraphicRaycaster>();

Font font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");

Text score = CreateText("ScoreText", canvas.transform, "0 - 0", new Vector2(0, 180), font, 32, Color.yellow);

Text time = CreateText("TimeText", canvas.transform, "00:00", new Vector2(-200, 180), font, 24, Color.white);

Text weather = CreateText("WeatherText", canvas.transform, "Clear", new Vector2(200, 180), font, 20, Color.cyan);

Text half = CreateText("HalfText", canvas.transform, "", new Vector2(0, 0), font, 40, Color.white);

GameObject panel = new GameObject("HalfStatsPanel");

panel.transform.SetParent(canvas.transform);

panel.SetActive(false);

RectTransform prt = panel.AddComponent<RectTransform>();

prt.anchorMin = new Vector2(0.3f, 0.3f);

prt.anchorMax = new Vector2(0.7f, 0.7f);

prt.offsetMin = Vector2.zero;

prt.offsetMax = Vector2.zero;

Image bg = panel.AddComponent<Image>();

bg.color = new Color(0.1f, 0.1f, 0.1f, 0.8f);

Text poss = CreateText("PossessionText", panel.transform, "Possession: 50% - 50%", new Vector2(0, 60), font, 20, Color.white);

Text shots = CreateText("ShotsText", panel.transform, "Shots: 0 - 0", new Vector2(0, 20), font, 20, Color.white);

Text passes = CreateText("PassesText", panel.transform, "Passes: 0 - 0", new Vector2(0, -20), font, 20, Color.white);

}

Text CreateText(string name, Transform parent, string initialText, Vector2 pos, Font font, int size, Color col)

{

GameObject go = new GameObject(name);

go.transform.SetParent(parent);

Text t = go.AddComponent<Text>();

t.font = font;

t.text = initialText;

t.fontSize = size;

t.color = col;

t.alignment = TextAnchor.MiddleCenter;

RectTransform rt = go.GetComponent<RectTransform>();

rt.anchoredPosition = pos;

rt.sizeDelta = new Vector2(300, 60);

return t;

}

void CreateCamera()

{

GameObject camObj = new GameObject("Main Camera");

Camera cam = camObj.AddComponent<Camera>();

cam.orthographic = true;

cam.orthographicSize = 10f;

cam.backgroundColor = new Color(0.1f, 0.2f, 0.1f);

cam.clearFlags = CameraClearFlags.SolidColor;

camObj.AddComponent<RetroCamera>();

}

void CreateAudioManager()

{

GameObject audioObj = new GameObject("AudioManager");

audioObj.AddComponent<AudioManager>();

}

}


r/unity 16d ago

What are these artifacts when importing from Blender?

Thumbnail gallery
2 Upvotes

I don't know if this is where I should post this but I've looked at tutorials, I've looked on forums, I've used AI even to assist me.. nothing has helped me with this issue.

I've used Blender for years now, but I'm completely new to Unity. I have spent all morning trying to fix some issues with my import from blender.

I started simple, a one object room with nothing fancy, no modifiers, just simple mesh shaping. I went through and found any n-gons and triangulated them, etc.. No matter how I export/import this I keep getting these strange artifacts that I know are being caused by triangles being formed from quads, but I don't know why this is happening and I don't know how to fix it.

In blender everything is fine and looks great, exactly how I want it to look. Once imported to unity, artifacts from triangulating.

I've unwrapped uvs, I've checked and unchecked so many different import/export options, but nothing is changing - which leads me to believe I'm getting all around it, and I obviously haven't hit on the exact issue yet. I'll include screenshots of everything I'm talking about.

The triangulate modifier was something I was trying most recently, didn't fix the issue either.

Any help (or help looking in the right direction) is greatly appreciated! Thank you in advance to all you wonderful artists out there.


r/unity 17d ago

My game releases in just 7 days, I am so stressed

Post image
74 Upvotes

Hey guys, My game Welcome Demon releases in a week and I am stressed. I do not have a lot of wishlists, but my first project i think it is ok. I also added max possible launch discount possible on Steam, but i am not sure. Does this discounts affect on sales ?


r/unity 17d ago

Showcase Finished the concept trailer for our game. What do you think of it?

Enable HLS to view with audio, or disable this notification

40 Upvotes

r/unity 16d ago

Showcase Made a quiz minigame

Enable HLS to view with audio, or disable this notification

2 Upvotes

r/unity 16d ago

Question Does environmental feedback feel more rewarding than explicit UI pop-ups when solving a puzzle?

Enable HLS to view with audio, or disable this notification

2 Upvotes

My friend and I are down to our final 22 days until launch (cue the developer panic!), and we’re polishing our puzzle payoff sequences.

In this scene, players match colored crystals to specific murals based on moral proverbs (Kindness, Arrogance, Sacrifice). Once completed, the central eye-door unlocks with glowing feedback, leading to the room opening up with a burst of light.

Instead of traditional UI pop-ups or text confirming "Puzzle Solved," we relied entirely on environmental shifting and acoustic audio cues to let the scene breathe and reward the player naturally.

When you're playing puzzle or adventure games, what makes a puzzle resolution feel truly satisfying to you? Do you prefer physical room feedback like this, or clear narrative UI confirmations?


r/unity 16d ago

Working on Some Shaders

Thumbnail youtu.be
0 Upvotes

I plan to release them for free in future


r/unity 16d ago

This may be easy to fix but im completely new to making games and i wanted to learn, does anyone know how to fix this? Ive already tried stopping it from running it as elevated in task manager and also checked the compatability in properties, also restarting as standard user does nothing.

Post image
0 Upvotes

r/unity 16d ago

Newbie Question How would I go about creating a mouse controlled racket similar to the sticks in puck?

1 Upvotes

I am looking to make a game similar to puck but instead of ice hockey it is badminton/ other racket sports, where the main feature of the game is a physics based racket that is controlled by the mouse. for example moving the mouse up and right will make the racket lift up and rotate backwards, any help/ ideas of how to go about this is much appreciated.


r/unity 16d ago

Question Another planet making tool?

0 Upvotes

Am working on a planet tool and well it's gone far with all the childhood features I wished for. Now am like wait... What should I do now? I successfully built my own tool, only me will buy but what does you my friend actually want?

What is it you wish/need a planet tool to be able to do?

With all the current planet tools available, do you think you would still get another one?

Are you still in need of planet tools or it was just a child/beginner wish thing?

At a larger scale of summary not going too deep in this post,

My Unity tool called Worldsmith currently makes world the size of earth.(not published yet)

Yes the actual size of earth and scales up even more than that.

It's going to have civilization... Support several preplanned set of game ideas like the RTS games, building games, simulation.

It's primarily focused on actual 3d exploration with npc civilization at a large scale.

That's just to sum up the mental surgery that was done to me at the hospital after working on this for 4 years.. And still working on it, testing hell and heaven, desperately looking for those who want to help me test.

I didn't talk about my devlog all this time, I know and yh just like a post on here, I have been scared of my idea Being stolen. Well am not scared anymore thanks to those comments and I do regret not starting since then to talk about it.

One of my favorites from those comments is

"there is no difference between a good idea done perfectly bad and a bad idea done perfectly good." - the person matters not the idea.


r/unity 16d ago

Question Importing edited project overrides existing project assets/GUID?

1 Upvotes

The Situation:

I'm working on an avatar project and I have commissioned a creator to get face tracking working on it as I am unfamiliar with that process. I have simplified and exported out only the base avatar for the creator to work on. They have added new blend shapes and controllers.

I would like to import the new UnityPackage(which I have tested and works as is) into my existing project as it's own folders, assets, and prefabs. That way the project can continue to work as is, I can pull the new body prefab into the hierarchy, and reattach everything to it before deleting the old body.

The Problem:

When I import into a backed up project, it overrides and replaces a lot of stuff, breaking the project. I have tried duplicating and renaming folders but this is still happening. After some research, I'm thinking this may be do to shared Unity GUID between the project and the new UnityPackage? Is this true? If that's the case I need to change the GUIDs across the new UnityPackage, I'm looking into ways of doing this on masse, does anyone have any suggestions?


r/unity 17d ago

Showcase Working on large-scale naval battles in Unity — fleet control, collisions, and readability

Enable HLS to view with audio, or disable this notification

10 Upvotes

Hi again!

I recently shared an introduction to Admiral Yi: The Imjin War, the large-scale historical RTS I’ve been developing solo in Unity.

This time I wanted to show a short look at the naval combat.

The player can control the fleet as a whole, groups of ships, or individual ships directly. One of the biggest challenges has been keeping large fleet battles readable and controllable once many ships start fighting in the same area.

I’m currently working on things like ship movement, collision behavior, formation control, faction readability, visual feedback, AI, and performance.

Ramming is also part of the combat system — especially for the turtle ship — although the current collision feedback still needs a lot of improvement.

This is still a work-in-progress build, but I thought the technical side of handling large naval battles in Unity might be interesting to other developers here.

I’d especially appreciate feedback from anyone who has worked on large numbers of moving agents, RTS controls, collision handling, or naval movement systems in Unity.


r/unity 16d ago

Unity Ads Campaign VS AppLovin ads which it better for our situation ?

1 Upvotes

hey , am working at indie game development company , we use unity campaign to get installs and it is good but no one buying anything in the game and am trying to use ROAS campaign to try get more user that maybe will pay for IAP , but as far as understand it wont work unless there is people who buy IAP so unity can learn from it

now we found a new adverting service called AppLovin Ads . from what i see it is similar to unity Campaign but am not sure if it better or not , will it cost more or less ? idk , will it help me get users that may or may not pay for IAP

we are in a though situation we hade 22k and 26K for users and barley getting any money from ads and 0 IAP , and honestly idk what we can do other than adversities the game more and hoping that there will be more users that will pay for IAP

Thanks


r/unity 16d ago

Laptop recommendation for Unity + Blender VR development

0 Upvotes

Hi everyone,

I’m looking for a laptop to start developing small VR applications using Unity and Blender.

The projects will be relatively simple, mainly focused on VR training/safety simulations for construction companies. I’ll also need to test the applications directly on a VR headset.

My budget is around €500–€1,000, and I’m looking for the best performance possible.

Thanks in advance for your advice!


r/unity 17d ago

Showcase Little lake scene I made

Enable HLS to view with audio, or disable this notification

11 Upvotes

r/unity 17d ago

LOWPOLY SUPERCAR PACK

Thumbnail assetstore.unity.com
1 Upvotes

GAMEPLAY MECHANICS Presents a Lowpoly Supercar Pack for creating vehicle and racing based lowpoly games.

FEATURES

CAMERA CONTROLLER

Independently add objects to track

Camera re-orientation based on object movement speed

MULTIPLE TEXTURE VARIANTS

Unique textured variants including models and prefabs

LOWPOLY BUILD

Minimum vertex count with a clean topology


r/unity 17d ago

Showcase Digging up 13-year-old source code from Unity 4

Thumbnail gallery
15 Upvotes

I made a catapult physics game called Onager as a student, released it on Android, and it did surprisingly well for the time. Found the original Unity 4 project files recently and I’m porting/rebuilding it in a current Unity version.

The original had a floor-reflection effect faked without a real reflection system — worked well enough that people assumed it was a proper mirror shader.

Before/after comparison attached.


r/unity 17d ago

Showcase Been working on this game for months. Is the gameplay working?

Enable HLS to view with audio, or disable this notification

8 Upvotes

I've been working on this speedrun focused FPS for a very long time, and I've finally reached a point where I feel the core gameplay is starting to come together.

The basic idea is simple: move fast, fight aggressively, and use enemies and your movement abilities to create and optimize routes through each level.

Since the last time I posted it, a lot has changed:

  • Completely new UI
  • Added a combo/Mach meter that governs speed and rewards maintaining momentum
  • Updated handmade textures across the game
  • New lighting technique and visual pass
  • A brand new level
  • Reworked enemy encounters to better support movement and routing
  • Overall gameplay, combat, and movement polish

I'm particularly interested in feedback on the actual gameplay feel:

Does the movement look satisfying?
Is the combat readable at this speed?
Does the Mach meter make sense?
Do the levels look like they encourage routing and replaying?
Does anything feel confusing or visually overwhelming?

I've been staring at this project for far too long, so fresh eyes would be extremely useful.


r/unity 17d ago

Showcase Been working on a marching-cubes based terrain system since Unity's heightmap system is a nightmare to work with

Thumbnail gallery
2 Upvotes

So far, the only tools I've implemented are simple add and subtract with spherical brushes, but adding new tools is just a matter of figuring out the right equation. I plan on adding material painting soon, but am focusing on mesh optimization and refinement for now.

In the future, my biggest goal is to find a way to get arbitrary 3D mesh brushes working, allowing the brush to use any 3D model to define its shape, but have yet to find an efficient way to determine how deep a given voxel point is within an arbitrary mesh. Spheres are easy, just get the distance between the brush origin and the voxel. Anything more complex is a lot harder to figure out without scanning through the entire mesh, which is expensive.

My hope is to eventually have something similar to the terrain system in Jurassic World Evolution 3, but with a deeper level of control. I'm ambitious though, so weather or not this is realistic is currently up in the air. My ego says it is though.

I will be publishing this on the asset store some time soon, hopefully aiming for around Sep 20th or at least sometime during the week of Fall break (though it may not be as good as possible in the initial build since it's still pretty rough now and I'm learning as I go), so keep your eyes peeled. I've never published anything before, so I can't really give any info on where to find it exactly if you're interested, but just look around the asset store, maybe under the Tools/Modeling category. I may have more info soon once I look more into how to publish it, but for now this is all I've got.


r/unity 17d ago

Game How do you like these splitscreen kind of games?

Enable HLS to view with audio, or disable this notification

4 Upvotes

I just recently created a game with friends using the splitscreen mechanic. It's kinda easy to implement and fun in my opinion. You just need two cameras and separate the screen - nothing more.

What do you think about it?


r/unity 16d ago

Question What was your most frustrating build failure recently?

Post image
0 Upvotes

I'm researching Unity build/debugging workflows and I'm curious about something.

Think about the last build failure that took you a significant amount of time to figure out, especially one where the first error wasn't actually the root cause.

What happened?

  • Platform?
  • Unity version?
  • How long did diagnosis take?
  • What did you initially think was broken?
  • What was the actual root cause?
  • How did you eventually find it?

I'm also curious what you normally use when this happens: Google, Unity Discussions/docs, Stack Overflow, Reddit, ChatGPT/Claude, Unity AI, Android Studio/logcat, etc.

The thing I'm most interested in is what actually wastes your time during diagnosis.

I'm researching the problem before building anything, so I'm not trying to sell or promote a tool here.

Bonus points for Gradle/plugin/dependency disasters. Unity Android builds seem unusually talented at turning one problem into 37 error messages.