A Python game demonstrating light refraction across different materials
import pygame_gui
from pygame_gui.elements import UITextEntryLine, UIButton, UILabel, UIDropDownMenu
import pygame
import math
import sys
import time
import traceback
import random
import os
import sys
if getattr(sys, 'frozen', False):
# Estamos en un ejecutable compilado con PyInstaller
bundle_dir = sys._MEIPASS
else:
# Estamos en el script original de Python
bundle_dir = os.path.dirname(os.path.abspath(__file__))
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Juego de refracción")
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
font = pygame.font.Font(None, 36)
def resource_path(relative_path):
try:
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
def ley_snell(n1, n2, angulo_incidente):
angulo_incidente = math.radians(angulo_incidente)
angulo_refractado = math.asin(n1 * math.sin(angulo_incidente) / n2)
return math.degrees(angulo_refractado)
def game_loop():
game_over = False
while not game_over:
n1 = 1.0 # Índice de refracción del aire (valor inicial)
n2 = 1.5 # Índice de refracción del vidrio (valor inicial)
angulo_incidente = random.randint(0, 90) # Generar un ángulo de incidencia aleatorio entre 0 y 90 grados
angulo_refractado_real = ley_snell(n1, n2, angulo_incidente)
intentos = 3
win = False
error_margin = 2
manager = pygame_gui.UIManager((WIDTH, HEIGHT))
text_input = UITextEntryLine(pygame.Rect(20, 260, 150, 30), manager)
# Crear un botón para reiniciar el juego
restart_button = UIButton(pygame.Rect(20, 300, 150, 30), 'Reiniciar', manager)
restart_button.hide()
# Crear una etiqueta y un menú desplegable para seleccionar el medio
medium_label = UILabel(pygame.Rect(20, 160, 150, 30), "Medio inicial", manager)
mediums = {
"Aire": 1.0,
"Agua": 1.33,
"Espacio": 1.0,
"Personalizado": -1
}
medium_dropdown = UIDropDownMenu(list(mediums.keys()), "Aire", pygame.Rect(180, 160, 150, 30), manager)
# Crear una etiqueta y un menú desplegable para seleccionar el tipo de vidrio
glass_type_label = UILabel(pygame.Rect(20, 200, 150, 30), "Tipo de vidrio", manager)
glass_types = {
"Vidrio común": 1.5,
"Vidrio flint": 1.6,
"Vidrio crown": 1.52,
"Personalizado": -1
}
glass_type_dropdown = UIDropDownMenu(list(glass_types.keys()), "Vidrio común", pygame.Rect(180, 200, 150, 30), manager)
# Crear una etiqueta y un campo de entrada de texto para el índice de refracción personalizado del medio
custom_medium_label = UILabel(pygame.Rect(350, 160, 200, 30), "n1 personalizado", manager)
custom_medium_label.hide()
custom_medium_entry = UITextEntryLine(pygame.Rect(560, 160, 150, 30), manager)
custom_medium_entry.hide()
# Crear una etiqueta y un campo de entrada de texto para el índice de refracción personalizado del vidrio
custom_glass_label = UILabel(pygame.Rect(350, 200, 200, 30), "n2 personalizado", manager)
custom_glass_label.hide()
custom_glass_entry = UITextEntryLine(pygame.Rect(560, 200, 150, 30), manager)
custom_glass_entry.hide()
clock = pygame.time.Clock()
game_loop_running = True
while game_loop_running:
time_delta = clock.tick(60) / 1000.0
screen.fill(WHITE)
events = pygame.event.get()
light_beam_image_path = resource_path("snell.png")
light_beam_image = pygame.image.load(light_beam_image_path)
screen.blit(light_beam_image, (420, 90))
for event in events:
if event.type == pygame.QUIT:
game_over = True
game_loop_running = False
manager.process_events(event)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
if intentos > 0:
try:
angulo_adivinado = int(text_input.get_text())
if abs(angulo_adivinado - angulo_refractado_real) <= error_margin:
win = True
game_loop_running = False
else:
intentos -= 1
if intentos == 0:
restart_button.show()
except ValueError:
# El usuario ingresó un valor no válido
pass
text_input.set_text('')
if event.type == pygame.USEREVENT:
if event.user_type == pygame_gui.UI_DROP_DOWN_MENU_CHANGED:
# Mostrar u ocultar campos de índice de refracción personalizado según la selección del menú desplegable
if event.ui_element == medium_dropdown:
if medium_dropdown.selected_option == "Personalizado":
custom_medium_label.show()
custom_medium_entry.show()
else:
custom_medium_label.hide()
custom_medium_entry.hide()
elif event.ui_element == glass_type_dropdown:
if glass_type_dropdown.selected_option == "Personalizado":
custom_glass_label.show()
custom_glass_entry.show()
else:
custom_glass_label.hide()
custom_glass_entry.hide()
if event.user_type == pygame_gui.UI_BUTTON_PRESSED:
if event.ui_element == restart_button:
# Reiniciar el juego sin llamar a game_loop() recursivamente
game_loop_running = False
manager.update(time_delta)
# Actualizar el índice de refracción del medio seleccionado
if medium_dropdown.selected_option == "Personalizado":
try:
n1 = float(custom_medium_entry.get_text())
except ValueError:
# El usuario ingresó un valor no válido
pass
else:
n1 = mediums[medium_dropdown.selected_option]
# Actualizar el índice de refracción del vidrio seleccionado
if glass_type_dropdown.selected_option == "Personalizado":
try:
n2 = float(custom_glass_entry.get_text())
except ValueError:
# El usuario ingresó un valor no válido
pass
else:
n2 = glass_types[glass_type_dropdown.selected_option]
# Recalcular el ángulo refractado
angulo_refractado_real = ley_snell(n1, n2, angulo_incidente)
# Actualizar la interfaz
texto = font.render("La luz pasa del medio (n1={:.2f}) al vidrio (n2={:.2f}).".format(n1, n2), True, BLACK)
screen.blit(texto, (20, 20))
texto = font.render(f"Ángulo de incidencia: {angulo_incidente} grados", True, BLACK)
screen.blit(texto, (20, 60))
texto = font.render(f"Intentos restantes: {intentos}", True, BLACK)
screen.blit(texto, (20, 100))
if win:
texto = font.render(f"¡Felicidades! Has adivinado el ángulo de refracción. El ángulo real es {angulo_refractado_real:.2f} grados.", True, BLACK)
screen.blit(texto, (20, 140))
restart_button.show()
elif intentos == 0:
texto = font.render(f"Has perdido. El ángulo de refracción real era {angulo_refractado_real:.2f} grados.", True, BLACK)
screen.blit(texto, (20, 140))
manager.draw_ui(screen)
pygame.display.flip()
game_loop()
pygame.quit()