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);
}
publicmethod can be called from external scriptsAddForce(): Apply physical force to Rigidbody for shootingdir: 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 totrue, 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 click0: 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 speedcurrentX: Track current X positiondirection: 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 byTime.deltaTimeensures 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
Rigidbodycomponent - Add
SphereCollidercomponent - Add
ParticleSystemcomponent (collision effect) - Add
BamsongiControllerscript - Save as prefab (drag to Project window)
Generator Object
- Create empty GameObject
- Add
BamsongiGeneratorscript - Connect prefab to
bamsongiPrefabvariable
Target Object
- Create target 3D model or Cube object
- Add
Collidercomponent (for collision detection) - Add
TargetControllerscript
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.