OFERTA Oferta = Stock + prod.Jugador Stock = prod....

Creado el: 26 de febrero de 2025

Respondido usando O1 por Chat01

Pregunta

OFERTA
Oferta = Stock + prod.Jugador
Stock = prod.NPC1 + prod.NPC2 + prod.NPC3
(ejemplo, mp = materia prima, Δ = numero aleatorio dentro de un rango (positivo o negativo))
prod.NPC1:
mp1 = x +- Δ
mp2 = y +- Δ
mp3 = z +- Δ
prod.Jugador = produccion x ciclo


DEMANDA
Demanda (para cada mp) = consumidores +- Δ1
consumidores = %poblacionTotal
poblacionTotal = poblacionAnterior +- Δ2
Δ1 = (eventos aleatorios) +- (temporada (invierno, verano...))
Δ2 = (eventos aleatorios) +- (temporada) +- Δ3
Δ3 = numero aleatorio (positivo o negativo) dentro de un rango


PRECIOS FINALES

precioMercado = precioBase x (1 + α x (Demanda - Oferta))

α = 0,1 (puede variar)
precioBase = depende de la materia prima.


como puedo implementar este sistema en mi juego Tycoon en unity? determina el precio de mercado de cada recurso cada 30 segundos (o "ciclo", ya que tengo un script que maneja cada ciclo y su duracion) en tres scripts si es posible (Demanda, Oferta y Precios). La cantidad de materias primas producidas por el jugador estan declaradas en otro script aparte que ya tengo (llamemosle "Recursos")

Pensamiento

Analyzing game mechanics

I’m curious about mapping out scripts for supply and demand in a Tycoon game. Considering Demand, Supply, and Price calculation as separate MonoBehaviour scripts, each running every 30 seconds. Fascinating!

Mapping out procedures

OK, let me see. The cycle is 30 seconds. DemandManager estimates population and events. OfferManager sums up NPC and player production. PriceManager uses these values to set new prices.

Setting up the cycle

Utilizing dictionaries or arrays to store outcomes. Consider loops or coroutines in a GameManager script for perpetual updates.

Crafting the scripts

I’m pulling together script examples for Demand, Offer, and Price, focusing on Unity's 'Update' or coroutines. Providing Spanish code for quantifying player production and resource types is also on the agenda.

Laying the foundation

I’m working through implementing Demand, Offer, and Price scripts, setting up arrays for resource demands, offers, and prices, and applying calculations and random deltas to ensure they update regularly.

Reasoned for 17 seconds

Respuesta

A continuación te propongo una forma de estructurar el sistema en tres scripts separados (Demanda, Oferta y Precios) más un pequeño “manager” que se encargue de orquestar el cálculo cada 30 segundos (o cada ciclo). Ten en cuenta que este es solo un ejemplo orientativo y puedes adaptarlo a tus necesidades concretas (por ejemplo, usar ScriptableObjects en lugar de MonoBehaviours, usar un único GameManager que agrupe todo, etc.).


Resumen de la lógica

  1. Demanda:

    • Calcula la población total.
    • Aplica los modificadores aleatorios (Δ1, Δ2, etc.) y la estacionalidad (invierno, verano, etc.).
    • Determina la demanda final por cada materia prima.
  2. Oferta:

    • Calcula la producción de NPCs (NPC1, NPC2, NPC3, …) para cada recurso con sus variaciones aleatorias.
    • Suma la producción del jugador (obtenida de tu script Recursos).
    • Determina la oferta final (Stock + producción del jugador).
  3. Precios:

    • Para cada recurso, calcula el precio de mercado usando la fórmula: precioMercado=precioBase×(1+α×(DemandaOferta)) \text{precioMercado} = \text{precioBase} \times (1 + \alpha \times (\text{Demanda} - \text{Oferta}))
    • Ajusta o limita el precio para evitar valores negativos o excesivamente altos (opcional).
  4. Actualización cada ciclo (30s):

    • Utilizas un “manager” (o un script central) que invoque los métodos de cálculo en orden y actualice los valores en el juego.
    • Se puede usar una Corrutina (Coroutine) con WaitForSeconds(30), un InvokeRepeating, o un temporizador manual.

Script 1: DemandManager

csharp
using UnityEngine; public class DemandManager : MonoBehaviour { // Demandas calculadas para cada materia prima. // Supongamos que tienes 3 materias primas: mp1, mp2, mp3 public float demandaMp1 { get; private set; } public float demandaMp2 { get; private set; } public float demandaMp3 { get; private set; } // Parámetros que influyen en la demanda [Header("Población")] public float poblacionActual = 1000f; // Se puede inicializar a un valor base public float porcentajeConsumidores = 0.5f; // Ej: 50% de la población consume la materia [Header("Rangos de variación aleatoria")] public Vector2 deltaPoblacion = new Vector2(-20f, 20f); // Variación de la población public Vector2 deltaDemanda = new Vector2(-10f, 10f); // Variación de la demanda final [Header("Ajustes estacionales/eventos")] public float factorInvierno = -0.1f; // -10% de la demanda public float factorVerano = 0.2f; // +20% de la demanda // ...y podrías tener otros factores u otros tipos de evento // Ejemplo simple para definir la temporada actual public enum Temporada { None, Invierno, Verano } public Temporada temporadaActual = Temporada.None; /// <summary> /// Método principal para calcular la demanda de cada recurso. /// Lo puedes llamar cada ciclo. /// </summary> public void CalcularDemanda() { // 1. Calcular población con variaciones aleatorias float variacionPoblacion = Random.Range(deltaPoblacion.x, deltaPoblacion.y); poblacionActual += variacionPoblacion; // Evitar población negativa, por si acaso if (poblacionActual < 0) poblacionActual = 0; // 2. Calcula la base de consumidores float baseConsumidores = poblacionActual * porcentajeConsumidores; // 3. Ajuste por temporada (opcional) float factorEstacional = 0f; switch (temporadaActual) { case Temporada.Invierno: factorEstacional = factorInvierno; break; case Temporada.Verano: factorEstacional = factorVerano; break; } // 4. Variación de la demanda (evento aleatorio) float variacionDemanda = Random.Range(deltaDemanda.x, deltaDemanda.y); // 5. Demanda final por recurso (puedes personalizar por recurso) demandaMp1 = (baseConsumidores + variacionDemanda) * (1 + factorEstacional); demandaMp2 = (baseConsumidores + variacionDemanda) * (1 + factorEstacional); demandaMp3 = (baseConsumidores + variacionDemanda) * (1 + factorEstacional); // Si quieres evitar demandas negativas: demandaMp1 = Mathf.Max(demandaMp1, 0); demandaMp2 = Mathf.Max(demandaMp2, 0); demandaMp3 = Mathf.Max(demandaMp3, 0); // Aquí podrías mostrarlo en consola para pruebas Debug.Log($"Demanda calculada -> mp1: {demandaMp1}, mp2: {demandaMp2}, mp3: {demandaMp3}"); } }

Notas:

  • Aquí suponemos que la demanda de los tres recursos se calcula de la misma manera. Podrías tener reglas diferentes por recurso.
  • Si quieres modelar distintos factores por recurso, puedes definir varias variables o tablas en lugar de usar la misma lógica para todos.

Script 2: SupplyManager (Oferta)

csharp
using UnityEngine; public class SupplyManager : MonoBehaviour { // Oferta total calculada public float ofertaMp1 { get; private set; } public float ofertaMp2 { get; private set; } public float ofertaMp3 { get; private set; } [Header("NPC Production Ranges")] public Vector2 npc1Mp1Range = new Vector2(10, 20); // Podría ser x +- Δ public Vector2 npc1Mp2Range = new Vector2(5, 15); public Vector2 npc1Mp3Range = new Vector2(1, 5); public Vector2 npc2Mp1Range = new Vector2(8, 25); public Vector2 npc2Mp2Range = new Vector2(5, 10); public Vector2 npc2Mp3Range = new Vector2(2, 6); public Vector2 npc3Mp1Range = new Vector2(7, 12); public Vector2 npc3Mp2Range = new Vector2(6, 18); public Vector2 npc3Mp3Range = new Vector2(3, 7); // Referencia a tu script de Recursos donde está la producción del jugador // Asumamos que en "Recursos" tienes propiedades: produccionMp1, produccionMp2, produccionMp3 public Recursos recursosDelJugador; public void CalcularOferta() { // 1. Calcular Stock (suma de producciones de NPC) float npc1Mp1 = Random.Range(npc1Mp1Range.x, npc1Mp1Range.y); float npc1Mp2 = Random.Range(npc1Mp2Range.x, npc1Mp2Range.y); float npc1Mp3 = Random.Range(npc1Mp3Range.x, npc1Mp3Range.y); float npc2Mp1 = Random.Range(npc2Mp1Range.x, npc2Mp1Range.y); float npc2Mp2 = Random.Range(npc2Mp2Range.x, npc2Mp2Range.y); float npc2Mp3 = Random.Range(npc2Mp3Range.x, npc2Mp3Range.y); float npc3Mp1 = Random.Range(npc3Mp1Range.x, npc3Mp1Range.y); float npc3Mp2 = Random.Range(npc3Mp2Range.x, npc3Mp2Range.y); float npc3Mp3 = Random.Range(npc3Mp3Range.x, npc3Mp3Range.y); float stockMp1 = npc1Mp1 + npc2Mp1 + npc3Mp1; float stockMp2 = npc1Mp2 + npc2Mp2 + npc3Mp2; float stockMp3 = npc1Mp3 + npc2Mp3 + npc3Mp3; // 2. Añadir producción del jugador // Asumiendo que en "Recursos" tienes algo como: recursosDelJugador.produccionMp1 ofertaMp1 = stockMp1 + recursosDelJugador.produccionMp1; ofertaMp2 = stockMp2 + recursosDelJugador.produccionMp2; ofertaMp3 = stockMp3 + recursosDelJugador.produccionMp3; Debug.Log($"Oferta calculada -> mp1: {ofertaMp1}, mp2: {ofertaMp2}, mp3: {ofertaMp3}"); } }

Notas:

  • En la vida real, “stock” a veces se refiere a la cantidad almacenada de turnos anteriores, pero según tu ejemplo, parece que Stock = prod.NPC1 + prod.NPC2 + prod.NPC3.
  • Ajusta los rangos npcXMpYRange según tu necesidad (esas son las variaciones ±Δ).
  • Asegúrate de arrastrar el script de Recursos (del jugador) a la variable recursosDelJugador en el inspector.

Script 3: PriceManager (Precios)

csharp
using UnityEngine; public class PriceManager : MonoBehaviour { [Header("Parámetros de Precios")] public float precioBaseMp1 = 10f; public float precioBaseMp2 = 20f; public float precioBaseMp3 = 5f; // Factor alfa (puede variar) public float alpha = 0.1f; // Precios finales public float precioMercadoMp1 { get; private set; } public float precioMercadoMp2 { get; private set; } public float precioMercadoMp3 { get; private set; } // Referencias a scripts de Demanda y Oferta public DemandManager demandManager; public SupplyManager supplyManager; public void CalcularPrecios() { // Usamos: precioMercado = precioBase * (1 + α * (Demanda - Oferta)) precioMercadoMp1 = precioBaseMp1 * (1 + alpha * (demandManager.demandaMp1 - supplyManager.ofertaMp1)); precioMercadoMp2 = precioBaseMp2 * (1 + alpha * (demandManager.demandaMp2 - supplyManager.ofertaMp2)); precioMercadoMp3 = precioBaseMp3 * (1 + alpha * (demandManager.demandaMp3 - supplyManager.ofertaMp3)); // Evitar valores negativos (opcional): precioMercadoMp1 = Mathf.Max(0.01f, precioMercadoMp1); precioMercadoMp2 = Mathf.Max(0.01f, precioMercadoMp2); precioMercadoMp3 = Mathf.Max(0.01f, precioMercadoMp3); Debug.Log($"Precios -> mp1: {precioMercadoMp1}, mp2: {precioMercadoMp2}, mp3: {precioMercadoMp3}"); } }

Notas:

  • Si Demanda - Oferta es grande y positivo, sube el precio; si es negativo, baja el precio.
  • alpha controla la sensibilidad del precio a la diferencia entre Demanda y Oferta.
  • Ajusta los precios base y el alfa según tus necesidades de balance.
  • En un juego real, quizás querrás más filtros o “suavizados” para que el precio no cambie de manera demasiado brusca.

Script 4 (opcional): MarketCycleManager

Para finalizar, puedes tener un script que centralice la llamada a estos tres sistemas cada 30 segundos. Por ejemplo:

csharp
using UnityEngine; using System.Collections; public class MarketCycleManager : MonoBehaviour { public DemandManager demandManager; public SupplyManager supplyManager; public PriceManager priceManager; [Header("Duración de cada ciclo en segundos")] public float cycleDuration = 30f; private void Start() { // Comenzamos la corrutina que cada 'cycleDuration' segundos recalcula todo StartCoroutine(MarketCycleRoutine()); } private IEnumerator MarketCycleRoutine() { while (true) { // 1. Calcular la Demanda demandManager.CalcularDemanda(); // 2. Calcular la Oferta supplyManager.CalcularOferta(); // 3. Calcular Precios priceManager.CalcularPrecios(); // Esperamos el siguiente ciclo yield return new WaitForSeconds(cycleDuration); } } }

Notas:

  • Asegúrate de arrastrar en el Inspector las referencias a DemandManager, SupplyManager y PriceManager.
  • Esta corrutina se repetirá indefinidamente.
  • Si prefieres, puedes usar InvokeRepeating("Metodo", 0, cycleDuration); en lugar de corrutina.

Integración con tu script “Recursos”

En tu script Recursos (o como lo llames), debes asegurarte de tener las propiedades públicas (o getters) que las otras clases necesitan para leer la producción del jugador. Ejemplo:

csharp
using UnityEngine; public class Recursos : MonoBehaviour { public float produccionMp1 = 10f; public float produccionMp2 = 5f; public float produccionMp3 = 2f; // Aquí va tu lógica de cómo el jugador produce recursos, // por ejemplo usando edificios, trabajadores, etc. }

Después, en el Inspector de tu escena, arrastras el objeto que tenga el script Recursos al campo recursosDelJugador en el SupplyManager.


Flujo final

  1. Cada 30s (o cada ciclo):
    1. DemandManager.CalcularDemanda() -> Población se actualiza, factores estacionales y aleatorios, se obtiene la Demanda para cada recurso.
    2. SupplyManager.CalcularOferta() -> Suma la producción del jugador (Recursos) y la de NPCs (con sus variaciones aleatorias), se obtiene la Oferta.
    3. PriceManager.CalcularPrecios() -> Con Demanda y Oferta ya calculadas, se genera el nuevo precio de mercado para cada recurso, usando la fórmula.
  2. Esos valores se pueden usar en el resto del juego para transacciones, UI, etc.
  3. Repetir.

Resumen

  • Script DemandManager se encarga exclusivamente de la lógica de demanda y población.
  • Script SupplyManager se encarga de la oferta (producción de NPC + jugador).
  • Script PriceManager aplica la fórmula de precios para cada recurso.
  • MarketCycleManager (opcional) orquesta los cálculos cada cierto tiempo (30s).

De esta manera, mantienes tu código modular, fácil de depurar y extender. Luego podrás ajustar fácilmente los factores aleatorios, las fórmulas de demanda y oferta, y la manera en la que presentas la información al jugador.

Compartir este Q&A