Initial commit: AIGame Unity client with URP, HybridCLR, YooAsset and tooling.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
udx
2026-09-21 14:21:07 +08:00
co-authored by Cursor
commit f3f66ab063
931 changed files with 112366 additions and 0 deletions
@@ -0,0 +1,248 @@
using UnityEngine;
using Unity.Pipeline.CodeReload;
// Pong sample for com.unity.pipeline in-place code reload:
// - Self-playing AI instead of the Input System (project has no com.unity.inputsystem).
// - Material created in code (project has no Resources/DefaultMaterial; built-in RP).
// - All members public: in-place [CodeReload] method bodies may only touch public members.
// - [CodeReload] on each editable method (Start/Update/PlacePaddle/OnDisable), so editing a helper
// like PlacePaddle takes effect too. `reload_file Assets/pong.cs`.
public class PongScript : MonoBehaviour
{
public GameObject leftPaddle, rightPaddle, ball;
public GameObject leftScoreGO, rightScoreGO, arenaGO;
public LineRenderer arena;
public TextMesh leftScoreText, rightScoreText;
public Material mat;
public Renderer ballRenderer;
public float leftPaddleAngle = 180f;
public float rightPaddleAngle = 0f;
public Vector3 ballVelocity;
public bool ballIsLeftPaddleColor = false;
public int leftLives = 3;
public int rightLives = 3;
public bool gameOver = false;
public float ballSpeed = 5f;
public float circleRadius = 2f;
public float paddleAngularSpeed = 180f;
public float gravity = 3f;
// Code-reload verification markers (read back via eval to confirm the live body ran).
public int updateTicks = 0;
public string marker = "none";
private Color leftColor ;
private Color rightColor;
[OnCodeReload]
public void OnReloaded()
{
Debug.Log($"Pong CodeReloaded");
OnDisable();
Start();
}
[CodeReload]
public void Start()
{
leftColor = new Color(0.678f, 1f, 0.184f); // green-yellow (Color.greenYellow needs 6000.2+)
rightColor = Color.magenta;
mat = new Material(Shader.Find("Sprites/Default"));
Camera cam = Camera.main;
cam.orthographic = true;
cam.orthographicSize = 12.5f;
cam.clearFlags = CameraClearFlags.SolidColor;
cam.backgroundColor = Color.grey;
cam.transform.position = new Vector3(0.0f, 0.0f, -10.0f);
leftScoreGO = new GameObject("LeftScore");
leftScoreGO.transform.position = new Vector3(-6f, 6f, 0f);
leftScoreText = leftScoreGO.AddComponent<TextMesh>();
leftScoreText.anchor = TextAnchor.MiddleCenter;
leftScoreText.alignment = TextAlignment.Center;
leftScoreText.text = "Cyan: " + leftLives.ToString();
leftScoreText.characterSize = 0.5f;
leftScoreText.color = leftColor;
rightScoreGO = new GameObject("RightScore");
rightScoreGO.transform.position = new Vector3(6f, 6f, 0f);
rightScoreText = rightScoreGO.AddComponent<TextMesh>();
rightScoreText.anchor = TextAnchor.MiddleCenter;
rightScoreText.alignment = TextAlignment.Center;
rightScoreText.text = "Magenta: " + rightLives.ToString();
rightScoreText.characterSize = 0.5f;
rightScoreText.color = rightColor;
arenaGO = new GameObject("CircleIndicator");
arenaGO.transform.position = new Vector3(0f, 0f, 0.5f);
arena = arenaGO.AddComponent<LineRenderer>();
arena.useWorldSpace = false;
arena.loop = true;
arena.startWidth = 0.1f;
arena.endWidth = 0.1f;
arena.material = mat;
arena.material.color = new Color(0.15f, 0.15f, 0.15f, 1f);
arena.positionCount = 64;
int i = 0;
while (i < 64)
{
float a = i * Mathf.PI * 2f / 64f;
arena.SetPosition(i, new Vector3(Mathf.Cos(a) * circleRadius, Mathf.Sin(a) * circleRadius, 0f));
i = i + 1;
}
leftPaddle = GameObject.CreatePrimitive(PrimitiveType.Quad);
leftPaddle.transform.localScale = new Vector3(0.3f, 1.5f, 1f);
Renderer lr = leftPaddle.GetComponent<Renderer>();
lr.material = mat;
lr.material.color = leftColor;
rightPaddle = GameObject.CreatePrimitive(PrimitiveType.Quad);
rightPaddle.transform.localScale = new Vector3(0.3f, 1.5f, 1f);
Renderer rr = rightPaddle.GetComponent<Renderer>();
rr.material = mat;
rr.material.color = rightColor;
PlacePaddle(leftPaddle, leftPaddleAngle);
PlacePaddle(rightPaddle, rightPaddleAngle);
ball = GameObject.CreatePrimitive(PrimitiveType.Sphere);
ball.transform.localScale = new Vector3(0.4f, 0.4f, 0.4f);
ballRenderer = ball.GetComponent<Renderer>();
ballRenderer.material = mat;
ballRenderer.material.color = rightColor;
ballVelocity = new Vector3(ballSpeed, ballSpeed * 0.5f, 0f);
}
[CodeReload]
public void PlacePaddle(GameObject paddle, float angleDeg)
{
float rad = angleDeg * Mathf.Deg2Rad;
paddle.transform.localScale = new Vector3(.5f,2,1);
paddle.transform.position = new Vector3(Mathf.Cos(rad) * circleRadius, Mathf.Sin(rad) * circleRadius, 0f);
paddle.transform.rotation = Quaternion.Euler(0f, 0f, angleDeg);
}
[CodeReload]
public void Update()
{
updateTicks++;
marker = "live";
if (gameOver)
{
this.OnDisable();
gameOver = false;
this.Start();
return;
}
float dt = Time.deltaTime;
// Self-playing AI: each paddle tracks the ball while the ball wears its colour.
float ballAng = Mathf.Atan2(ball.transform.position.y, ball.transform.position.x) * Mathf.Rad2Deg;
if (ballIsLeftPaddleColor)
{
float diff = Mathf.DeltaAngle(leftPaddleAngle, ballAng);
if (diff > 5f) leftPaddleAngle += paddleAngularSpeed * dt;
if (diff < -5f) leftPaddleAngle -= paddleAngularSpeed * dt;
}
else
{
float diff = Mathf.DeltaAngle(rightPaddleAngle, ballAng);
if (diff > 5f) rightPaddleAngle += paddleAngularSpeed * dt;
if (diff < -5f) rightPaddleAngle -= paddleAngularSpeed * dt;
}
PlacePaddle(leftPaddle, leftPaddleAngle);
PlacePaddle(rightPaddle, rightPaddleAngle);
ballVelocity = ballVelocity + Vector3.down * gravity * dt * 1.2f;
ball.transform.position = ball.transform.position + ballVelocity * dt ;
Vector3 bPos = ball.transform.position;
float bDist = bPos.magnitude;
float bAng = Mathf.Atan2(bPos.y, bPos.x) * Mathf.Rad2Deg;
if (bDist > circleRadius - 0.3f && bDist < circleRadius + 0.2f)
{
Vector3 normal = bPos.normalized;
if (Vector3.Dot(ballVelocity, normal) > 0f)
{
if (ballIsLeftPaddleColor)
{
float diff = Mathf.DeltaAngle(bAng, leftPaddleAngle);
if (diff > -12f && diff < 12f)
{
ballVelocity = Vector3.Reflect(ballVelocity, -normal);
ball.transform.position = ball.transform.position - normal * 0.1f;
ballIsLeftPaddleColor = false;
ballRenderer.material.color = rightColor;
}
}
else
{
float diff = Mathf.DeltaAngle(bAng, rightPaddleAngle);
if (diff > -12f && diff < 12f)
{
ballVelocity = Vector3.Reflect(ballVelocity, -normal);
ball.transform.position = ball.transform.position - normal * 0.1f;
ballIsLeftPaddleColor = true;
ballRenderer.material.color = leftColor;
}
}
}
}
if (bDist > 8f)
{
if (ballIsLeftPaddleColor)
{
leftLives -= 1;
leftScoreText.text = "Cyan: " + leftLives.ToString();
}
else
{
rightLives -= 1;
rightScoreText.text = "Magenta: " + rightLives.ToString();
}
if (leftLives <= 0 || rightLives <= 0)
{
gameOver = true;
}
else
{
ball.transform.position = new Vector3(0f, 0f, 0f);
ballVelocity = new Vector3(ballSpeed, ballSpeed * 0.5f, 0f);
ballIsLeftPaddleColor = rightLives < leftLives;
if (ballIsLeftPaddleColor)
{
ballRenderer.material.color = leftColor;
}
else
{
ballRenderer.material.color = rightColor;
}
}
}
}
[CodeReload]
public void OnDisable()
{
if (leftPaddle) GameObject.Destroy(leftPaddle);
if (rightPaddle) GameObject.Destroy(rightPaddle);
if (ball) GameObject.Destroy(ball);
if (leftScoreGO) GameObject.Destroy(leftScoreGO);
if (rightScoreGO) GameObject.Destroy(rightScoreGO);
if (arenaGO) GameObject.Destroy(arenaGO);
Camera.main.orthographicSize = 5.0f;
Camera.main.clearFlags = CameraClearFlags.Skybox;
Camera.main.orthographic = false;
}
}