Unity 6의 Build Profile (예: Android Debug, Release, iOS Dev 등)을 코드로 선택하거나 전환
카테고리 없음 2025. 4. 8. 13:301. Build Configuration 사용하기 (com.unity.build)
Unity 2023+에서 도입된 Build Configuration (빌드 구성) 시스템을 Unity 6에서도 사용 가능할 것으로 보입니다. 이 시스템은 ScriptableObject 형태의 빌드 프로파일을 지원하며, 이를 코드로 제어할 수 있습니다.
🔧 예시: Build Configuration을 로드하고 빌드
2. 기존 방식: BuildPipeline.BuildPlayer + EditorUserBuildSettings
Build Profiles가 복잡하게 연결되지 않은 단순 빌드라면, 여전히 BuildPipeline.BuildPlayer로 해결 가능하며, 플랫폼/옵션을 코드로 설정할 수 있습니다.
📦 예시: Android Release 빌드 설정
3. Build Profiles 직접 제어 (Build Configuration API 내부 구조 활용)
Build Configuration이 내부적으로 ScriptableObject이므로, 여러 개의 .buildconfiguration 파일을 관리하고 선택적으로 사용하면 사실상 Build Profile 선택 기능을 구현할 수 있습니다.
필요한 구성만 필터링해서 사용할 수 있습니다.
✅ 요약
Build Configuration API | .buildconfiguration 파일을 코드에서 불러와서 빌드 | 권장 (Unity 6 대응) |
BuildPipeline.BuildPlayer | 커스텀 코드 기반 빌드 수행 | 단순한 빌드에 적합 |
EditorUserBuildSettings | 플랫폼 전환, 개발모드 설정 등 제어 | 보조적으로 사용 가능 |
function redirectPage() {
const isMobile = /Mobi|Android|iPhone|iPad/i.test(navigator.userAgent);
const isLandscape = window.matchMedia("(orientation: landscape)").matches;
if (isMobile && isLandscape) {
window.location.href = "mobile-landscape.html"; // 모바일 가로용 페이지
} else if (!isMobile) {
window.location.href = "desktop.html"; // PC용 페이지
} else {
window.location.href = "mobile.html"; // 모바일 세로용 페이지
}
}
// 페이지 로드 시 실행
redirectPage();
// 화면 크기 변경 시 다시 체크 (예: 모바일에서 가로/세로 전환)
window.addEventListener("resize", redirectPage);
cursor ai 가 내가 원하는 스크립트 언어을 만들어줌, 아직 수정할께 많은데 이러면 최적의 스크립트 언어을 만들어 쓰는게 유리할수도 있음
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
public class ScriptLanguage
{
private Dictionary<string, object> variables = new Dictionary<string, object>();
private Dictionary<string, List<string>> functions = new Dictionary<string, List<string>>();
private Dictionary<string, ScriptLanguage> modules = new Dictionary<string, ScriptLanguage>();
public void ExecuteScript(string script)
{
string[] lines = script.Split('\n');
int currentLine = 0;
while (currentLine < lines.Length)
{
string line = lines[currentLine].Trim();
if (!string.IsNullOrEmpty(line) && !line.StartsWith("--"))
{
currentLine = ExecuteLine(line, lines, currentLine);
}
currentLine++;
}
}
private int ExecuteLine(string line, string[] lines, int currentLine)
{
if (line.StartsWith("function"))
{
string funcName = line.Split(' ')[1].Split('(')[0].Trim();
List<string> funcBody = new List<string>();
currentLine++;
while (currentLine < lines.Length && !lines[currentLine].Trim().StartsWith("end"))
{
funcBody.Add(lines[currentLine].Trim());
currentLine++;
}
functions[funcName] = funcBody;
return currentLine;
}
else if (line.StartsWith("if"))
{
string condition = line.Substring(2).Trim();
bool result = EvaluateCondition(condition);
if (!result)
{
while (currentLine < lines.Length && !lines[currentLine].Trim().StartsWith("end"))
{
currentLine++;
}
}
return currentLine;
}
else if (line.StartsWith("for"))
{
string[] parts = line.Substring(3).Split('=');
string varName = parts[0].Trim();
string[] range = parts[1].Split(' ');
int start = int.Parse(range[0]);
int end = int.Parse(range[2]);
for (int i = start; i <= end; i++)
{
variables[varName] = i;
int tempLine = currentLine + 1;
while (tempLine < lines.Length && !lines[tempLine].Trim().StartsWith("end"))
{
ExecuteLine(lines[tempLine].Trim(), lines, tempLine);
tempLine++;
}
}
while (currentLine < lines.Length && !lines[currentLine].Trim().StartsWith("end"))
{
currentLine++;
}
return currentLine;
}
else if (line.StartsWith("import"))
{
string moduleName = line.Substring(6).Trim();
ScriptLanguage module = new ScriptLanguage();
modules[moduleName] = module;
return currentLine;
}
else if (line.Contains("="))
{
string[] parts = line.Split('=');
string varName = parts[0].Trim();
string value = parts[1].Trim();
if (value.Contains("+") || value.Contains("-") || value.Contains("*") || value.Contains("/"))
{
variables[varName] = EvaluateExpression(value);
}
else if (value.StartsWith("\"") && value.EndsWith("\""))
{
variables[varName] = value.Trim('"');
}
else if (float.TryParse(value, out float numValue))
{
variables[varName] = numValue;
}
}
else if (line.StartsWith("print"))
{
string content = line.Substring(5).Trim();
content = content.Trim('"');
Debug.Log(content);
}
else if (functions.ContainsKey(line.Split('(')[0].Trim()))
{
string funcName = line.Split('(')[0].Trim();
foreach (string funcLine in functions[funcName])
{
ExecuteLine(funcLine, new string[] { funcLine }, 0);
}
}
return currentLine;
}
private float EvaluateExpression(string expression)
{
expression = expression.Replace(" ", "");
if (expression.Contains("+"))
{
string[] parts = expression.Split('+');
return float.Parse(parts[0]) + float.Parse(parts[1]);
}
else if (expression.Contains("-"))
{
string[] parts = expression.Split('-');
return float.Parse(parts[0]) - float.Parse(parts[1]);
}
else if (expression.Contains("*"))
{
string[] parts = expression.Split('*');
return float.Parse(parts[0]) * float.Parse(parts[1]);
}
else if (expression.Contains("/"))
{
string[] parts = expression.Split('/');
return float.Parse(parts[0]) / float.Parse(parts[1]);
}
return 0;
}
private bool EvaluateCondition(string condition)
{
if (condition.Contains("=="))
{
string[] parts = condition.Split(new[] { "==" }, System.StringSplitOptions.None);
return float.Parse(parts[0].Trim()) == float.Parse(parts[1].Trim());
}
else if (condition.Contains(">"))
{
string[] parts = condition.Split('>');
return float.Parse(parts[0].Trim()) > float.Parse(parts[1].Trim());
}
else if (condition.Contains("<"))
{
string[] parts = condition.Split('<');
return float.Parse(parts[0].Trim()) < float.Parse(parts[1].Trim());
}
return false;
}
public object GetVariable(string name)
{
return variables.ContainsKey(name) ? variables[name] : null;
}
public ScriptLanguage GetModule(string name)
{
return modules.ContainsKey(name) ? modules[name] : null;
}
}
-- 수학 연산자 예제
a = 10
b = 5
result = a + b
print "덧셈 결과: " + result
result = a * b
print "곱셈 결과: " + result
-- 조건문 예제
if a > b
print "a가 b보다 큽니다"
end
-- 반복문 예제
for i = 1 5
print "반복 횟수: " + i
end
-- 함수 정의와 호출 예제
function greet(name)
print "안녕하세요, " + name + "님!"
end
greet("플레이어")
-- 모듈 예제
import math
math.a = 100
math.b = 200
print "모듈 변수 a: " + math.a
print "모듈 변수 b: " + math.b
using System;
using System.Text;
using NATS.Client;
using NATS.Net;
using Systehttp://m.Threading.Tasks;
using NATS.Client.Core;
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
public class NatSample : MonoBehaviour
{
private NatsClient client;
private const string NATS_URL = "ws://35.208.138.76:8282"; // NATS 서버 주소
private Dictionary<string, IAsyncSubscription<string>> subscriptions = new Dictionary<string, IAsyncSubscription<string>>();
async void Start()
{
await ConnectToNats();
}
async Task ConnectToNats()
{
try
{
client = new NatsClient(new NatsOpts { Url = NATS_URL });
Debug.Log("Connected to NATS server");
}
catch (Exception e)
{
Debug.LogError($"NATS Connection Error: {e.Message}");
}
}
public async Task AddSubject(string subject)
{
if (client == null || subscriptions.ContainsKey(subject))
return;
try
{
var subscription = client.SubscribeAsync<string>(subject);
subscriptions[subject] = subscription;
_ = Task.Run(async () =>
{
await foreach (var msg in subscription)
{
Debug.Log($"Received on {subject}: {msg.Data}");
UpdateChatDisplay($"{subject}: {msg.Data}");
}
});
Debug.Log($"Added subscription to subject: {subject}");
}
catch (Exception e)
{
Debug.LogError($"Error adding subject {subject}: {e.Message}");
}
}
public async Task RemoveSubject(string subject)
{
if (client == null || !subscriptions.ContainsKey(subject))
return;
try
{
await subscriptions[subject].DisposeAsync();
subscriptions.Remove(subject);
Debug.Log($"Removed subscription from subject: {subject}");
}
catch (Exception e)
{
Debug.LogError($"Error removing subject {subject}: {e.Message}");
}
}
public async Task SendMessage(string subject, string message)
{
if (client == null) return;
try
{
await client.PublishAsync(subject, message);
Debug.Log($"Sent to {subject}: {message}");
}
catch (Exception e)
{
Debug.LogError($"Error sending message: {e.Message}");
}
}
void UpdateChatDisplay(string message)
{
Debug.Log("msg:" + message);
}
async void OnApplicationQuit()
{
if (client != null)
{
foreach (var subscription in subscriptions.Values)
{
await subscription.DisposeAsync();
}
await client.DisposeAsync();
}
}
}
using System;
using System.Text;
using NATS.Client;
using NATS.Net;
using System.Threading.Tasks;
using NATS.Client.Core;
using UnityEngine;
using UnityEngine.UI;
public class NatSample : MonoBehaviour
{
private NatsClient client;
//private const string NATS_URL = "nats://localhost:4222"; // NATS 서버 주소
private const string NATS_URL = "ws://localhost:8282"; // NATS 서버 주소
private const string SUBJECT = "test"; // 채팅 채널
async void Start()
{
await ConnectToNats();
}
async Task ConnectToNats()
{
try
{
client = new NatsClient(new NatsOpts { Url = NATS_URL });
await foreach (var msg in client.SubscribeAsync<string>(SUBJECT))
{
Debug.Log($"Received: {msg}");
UpdateChatDisplay(msg.Data);
}
}
catch (Exception e)
{
Debug.LogError($"NATS Connection Error: {e.Message}");
}
}
float t = 0.0f;
private void Update()
{
t += Time.deltaTime;
if (t>3)
{
t = 0;
SendMessage2();
}
}
public async void SendMessage2()
{
if (client == null ) return;
await client.PublishAsync(SUBJECT, "1a2a");
}
void UpdateChatDisplay(string message)
{
Debug.Log("msg:"+ message);
}
async void OnApplicationQuit()
{
if (client != null)
{
await client.DisposeAsync();
}
}
}
import yt_dlp
def download_video(url, output_path="downloads/%(title)s.%(ext)s"):
ydl_opts = {
"outtmpl": output_path, # 파일 저장 경로 및 이름 설정
"format": "bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]", # 최고 화질 비디오+오디오 조합
"noplaylist": True, # 개별 비디오만 다운로드
"merge_output_format": "mp4", # 병합 시 MP4로 저장
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
# 예제 실행
video_url = "https://www.youtube.com/watch?v="
download_video(video_url)
websocket을 사용하려면 따로 설정파일 세팅해야함
websocket {
# Specify a host and port to listen for websocket connections
#
# listen: "host:port"
# It can also be configured with individual parameters,
# namely host and port.
#
# host: "hostname"
# port: 443
port: 8282
# This will optionally specify what host:port for websocket
# connections to be advertised in the cluster.
#
# advertise: "host:port"
# TLS configuration is required by default
#
#tls {
#cert_file: "/path/to/cert.pem"
#key_file: "/path/to/key.pem"
#}
# For test environments, you can disable the need for TLS
# by explicitly setting this option to `true`
#
no_tls: true
# [Cross-origin resource sharing option](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
#
# IMPORTANT! This option is used only when the http request presents an Origin
# header, which is the case for web browsers. If no Origin header is present,
# this check will not be performed.
#
# When set to `true`, the HTTP origin header must match the request’s hostname.
# The default is `false`.
#
#same_origin: true
# [Cross-origin resource sharing option](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
#
# IMPORTANT! This option is used only when the http request presents an Origin
# header, which is the case for web browsers. If no Origin header is present,
# this check will not be performed.
#
# List of accepted origins. When empty, and `same_origin` is `false`, clients from any origin are allowed to connect.
# This list specifies the only accepted values for the client's request Origin header. The scheme,
# host and port must match. By convention, the absence of TCP port in the URL will be port 80
# for an "http://" scheme, and 443 for "https://".
#
# allowed_origins [
# "http://www.example.com"
# "https://www.other-example.com"
# ]
# This enables support for compressed websocket frames
# in the server. For compression to be used, both server
# and client have to support it.
#
# compression: true
# This is the total time allowed for the server to
# read the client request and write the response back
# to the client. This includes the time needed for the
# TLS handshake.
#
# handshake_timeout: "2s"
# Name for an HTTP cookie, that if present will be used as a client JWT.
# If the client specifies a JWT in the CONNECT protocol, this option is ignored.
# The cookie should be set by the HTTP server as described [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies).
# This setting is useful when generating NATS `Bearer` client JWTs as the
# result of some authentication mechanism. The HTTP server after correct
# authentication can issue a JWT for the user, that is set securely preventing
# access by unintended scripts. Note these JWTs must be [NATS JWTs](https://docs.nats.io/nats-server/configuration/securing_nats/jwt).
#
# jwt_cookie: "my_jwt_cookie_name"
# If no user name is provided when a websocket client connects, will default
# this user name in the authentication phase. If specified, this will
# override, for websocket clients, any `no_auth_user` value defined in the
# main configuration file.
# Note that this is not compatible with running the server in operator mode.
#
# no_auth_user: "my_username_for_apps_not_providing_credentials"
# See below to know what is the normal way of limiting websocket clients
# to specific users.
# If there are no users specified in the configuration, this simple authorization
# block allows you to override the values that would be configured in the
# equivalent block in the main section.
#
# authorization {
# # If this is specified, the client has to provide the same username
# # and password to be able to connect.
# # username: "my_user_name"
# # password: "my_password"
#
# # If this is specified, the password field in the CONNECT has to
# # match this token.
# # token: "my_token"
#
# # This overrides the main's authorization timeout. For consistency
# # with the main's authorization configuration block, this is expressed
# # as a number of seconds.
# # timeout: 2.0
#}
}
html 소스
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NATS WebSocket Client</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; }
input, button { margin: 5px; padding: 10px; }
#messages { border: 1px solid #ccc; padding: 10px; width: 300px; margin: auto; height: 200px; overflow-y: scroll; }
</style>
</head>
<body>
<h2>NATS WebSocket Client</h2>
<div>
<input type="text" id="messageInput" placeholder="Enter message">
<button id="sendButton">Send</button> </div>
<h3>Received Messages</h3>
<div id="messages"></div>
<script type="module">
import { connect, StringCodec, JSONCodec } from "https://unpkg.com/nats.ws@latest/esm/nats.js";
const nc = await connect({ servers: "ws://localhost:8282" });
console.log("NATS에 연결됨");
const sc = StringCodec();
const jc = JSONCodec();
const subject = "test";
const sub = nc.subscribe(subject);
(async () => {
for await (const m of sub) {
try {
// Assuming the message is a string
const decodedString = sc.decode(m.data);
displayMessage(decodedString);
} catch (err){
//if can't decode as string it will be decoded as json
const decodedJson = jc.decode(m.data)
displayMessage(JSON.stringify(decodedJson))
}
}
})();
async function sendMessage() {
let message = document.getElementById("messageInput").value;
if (!message) return;
try {
//nc.publish(subject, sc.encode(message))
nc.publish(subject, jc.encode({data: message}))
displayMessage(`(Sent) ${message}`, true);
document.getElementById("messageInput").value = "";
} catch (err) {
console.error("Message send error", err);
}
}
// Get the button element
const sendButton = document.getElementById("sendButton");
// Add an event listener to the button
sendButton.addEventListener("click", sendMessage);
function displayMessage(msg, isSent = false) {
let msgDiv = document.getElementById("messages");
let newMessage = document.createElement("div");
newMessage.textContent = isSent ? `📤 ${msg}` : `📩 ${msg}`;
msgDiv.appendChild(newMessage);
msgDiv.scrollTop = msgDiv.scrollHeight;
}
// Handle connection close
nc.closed()
.then(() => {
console.log("NATS connection closed.");
})
.catch((err) => {
console.error("NATS connection closed with error:", err);
});
</script>
</body>
</html>