Chestnut Throwing Game

Implement a game where you shoot projectiles with mouse clicks to hit moving targets in Unity 3D

Let's create a game in Unity 3D where you shoot projectiles (chestnuts) with mouse clicks to hit moving targets. We'll learn about Ray-based mouse coordinate conversion, prefab instantiation, and physics-based shooting.


Game Structure

This game consists of three core scripts:

Script Role
BamsongiController Chestnut (projectile) shooting, collision handling, particle effects
BamsongiGenerator Create and shoot chestnuts on mouse click
TargetController Target moving back and forth horizontally

BamsongiController - Projectile Controller

A script that handles shooting and collision processing for the chestnut (projectile).

Complete Code

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class BamsongiController : MonoBehaviour
{
    void Start()
    {
        // Shoot(new Vector3(0, 250, 2000));
    }

    public void Shoot(Vector3 dir)
    {
        GetComponent<Rigidbody>().AddForce(dir);
    }

    private void OnCollisionEnter(Collision collision)
    {
        GetComponent<Rigidbody>().isKinematic = true;
        GetComponent<ParticleSystem>().Play();

        transform.parent = collision.transform;

        GetComponent<SphereCollider>().enabled = false;

        // Delete chestnut object
        Destroy(gameObject, 5.0f);
    }
}

Key Concepts Explained

1. Public Method for Shooting

public void Shoot(Vector3 dir)
{
    GetComponent<Rigidbody>().AddForce(dir);
}
  • public method can be called from external scripts
  • AddForce(): Apply physical force to Rigidbody for shooting
  • dir: Vector containing direction and magnitude of force

2. Collision Detection and Handling

private void OnCollisionEnter(Collision collision)
{
    GetComponent<Rigidbody>().isKinematic = true;
    GetComponent<ParticleSystem>().Play();
    transform.parent = collision.transform;
    GetComponent<SphereCollider>().enabled = false;
    Destroy(gameObject, 5.0f);
}
Code Description
isKinematic = true Disable physics simulation (no longer affected by gravity/forces)
ParticleSystem.Play() Play particle effect on collision
transform.parent = collision.transform Set as child of collided object (stuck to target)
SphereCollider.enabled = false Disable collider (prevent additional collisions)
Destroy(gameObject, 5.0f) Delete object after 5 seconds

What is isKinematic?
When set to true, the Rigidbody is not affected by the physics engine and can only be controlled by scripts or animations. This prevents the chestnut from falling after sticking to the target.


BamsongiGenerator - Projectile Generator

A script that creates chestnuts and shoots them toward the click direction on mouse click.

Complete Code

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BamsongiGenerator : MonoBehaviour
{
    public GameObject bamsongiPrefab;

    void Update()
    {
        // Create and shoot chestnut toward clicked screen position
        if (Input.GetMouseButtonDown(0))
        {
            GameObject bamsongi = Instantiate(bamsongiPrefab);

            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            Vector3 worldDir = ray.direction;
            bamsongi.GetComponent<BamsongiController>().Shoot(worldDir.normalized * 2000);
        }
    }
}

Key Concepts Explained

1. Prefab Reference

public GameObject bamsongiPrefab;
  • Drag and connect chestnut prefab in Inspector
  • Prefab: Pre-made game object template

2. Mouse Click Detection

if (Input.GetMouseButtonDown(0))
  • Input.GetMouseButtonDown(0): Left mouse button click
  • 0: Left, 1: Right, 2: Middle button

3. Prefab Instantiation

GameObject bamsongi = Instantiate(bamsongiPrefab);
  • Instantiate(): Clone prefab to create new game object
  • Returns reference to created object

4. Direction Calculation Using Ray (Key Concept!)

Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Vector3 worldDir = ray.direction;
bamsongi.GetComponent<BamsongiController>().Shoot(worldDir.normalized * 2000);
Code Description
Camera.main Reference to main camera
ScreenPointToRay() Convert screen coordinates to Ray in 3D space
Input.mousePosition Mouse screen coordinates (in pixels)
ray.direction Direction vector of Ray
worldDir.normalized * 2000 Normalize then apply force magnitude

What is a Ray?
A Ray is an infinite line with a starting point and direction. It extends from the camera toward the mouse click position, used to determine mouse click position in 3D space.

Screen Coordinates → World Coordinates Conversion Process

Mouse Click (Screen Coordinates)
    ↓
Input.mousePosition (Pixels: e.g., 640, 480)
    ↓
Camera.main.ScreenPointToRay()
    ↓
Ray (Light ray from camera toward click direction)
    ↓
ray.direction (Direction vector in world coordinates)
    ↓
Used as shooting direction

TargetController - Target Controller

A script that moves the target back and forth horizontally.

Complete Code

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TargetController : MonoBehaviour
{
    // Speed managed as variable
    float speed = 3.0f;

    // Track current x position and direction
    float currentX = 0f;
    float direction = 1f; // 1: right, -1: left

    void Update()
    {
        // Update x position using increment operator
        currentX += speed * direction * Time.deltaTime;
        
        // Reverse direction when reaching boundary
        if (currentX >= 20f)
        {
            currentX = 20f;
            direction = -1f; // Switch to left
        }
        else if (currentX <= -20f)
        {
            currentX = -20f;
            direction = 1f; // Switch to right
        }
        
        // Move target object between x +20 and -20 repeatedly
        transform.position = new Vector3(currentX, transform.position.y, transform.position.z);
    }
}

Key Concepts Explained

1. Movement Variable Declaration

float speed = 3.0f;
float currentX = 0f;
float direction = 1f; // 1: right, -1: left
  • speed: Movement speed
  • currentX: Track current X position
  • direction: Movement direction (1 or -1)

2. Frame-Independent Movement Using Time.deltaTime

currentX += speed * direction * Time.deltaTime;
  • Time.deltaTime: Time from previous frame to current frame (in seconds)
  • Moves at constant speed regardless of frame rate

What happens without Time.deltaTime?
Moves faster on high-performance PCs and slower on low-performance PCs. Multiplying by Time.deltaTime ensures consistent speed across all environments.

3. Boundary Check and Direction Reversal

if (currentX >= 20f)
{
    currentX = 20f;
    direction = -1f;
}
else if (currentX <= -20f)
{
    currentX = -20f;
    direction = 1f;
}
  • Reverse direction when X coordinate exceeds ±20
  • Clamp to boundary value to prevent exceeding range

4. Apply Position

transform.position = new Vector3(currentX, transform.position.y, transform.position.z);
  • Only change X axis, keep existing Y and Z values
  • Create new position vector with new Vector3()

Unity Setup Checklist

Chestnut Prefab (Bamsongi Prefab)

  • Create 3D model or Sphere object
  • Add Rigidbody component
  • Add SphereCollider component
  • Add ParticleSystem component (collision effect)
  • Add BamsongiController script
  • Save as prefab (drag to Project window)

Generator Object

  • Create empty GameObject
  • Add BamsongiGenerator script
  • Connect prefab to bamsongiPrefab variable

Target Object

  • Create target 3D model or Cube object
  • Add Collider component (for collision detection)
  • Add TargetController script

Camera Setup

  • Set "MainCamera" tag on Main Camera (default)
  • Position camera to see target and shooting position

Key Concepts Summary

3D vs 2D Differences

Item 2D 3D
Physics Component Rigidbody2D Rigidbody
Collision Component Collider2D Collider
Collision Callback OnCollisionEnter2D OnCollisionEnter
Coordinates Vector2 Vector3

Ray Usage Patterns

// Create Ray to mouse click position
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

// Use Ray direction (shooting direction)
Vector3 direction = ray.direction;

// Detect collision with Raycast
if (Physics.Raycast(ray, out RaycastHit hit))
{
    // hit.point: Collision point
    // hit.collider: Collided collider
}

Rigidbody.isKinematic Usage

isKinematic Behavior
false (default) Controlled by physics engine (gravity, collision response)
true Controlled by script/animation (physics ignored)

Summary

Item Content
Projectile Shooting Rigidbody.AddForce(direction)
Prefab Creation Instantiate(prefab)
Mouse → Ray Camera.main.ScreenPointToRay(Input.mousePosition)
Fix After Collision Rigidbody.isKinematic = true
Attach to Object transform.parent = collision.transform
Delayed Deletion Destroy(gameObject, 5.0f)
Back-and-Forth Movement Reverse direction with direction variable
Frame-Independent Movement speed * Time.deltaTime

By combining these three scripts, you can implement a 3D shooting game where you shoot projectiles with mouse clicks to hit moving targets.


← Back to Contents