USER
welches skript ist besser umgesetzt? das erste oder zweite?
import random
import numpy as np
from ursina import *
from opensimplex import OpenSimplex
import math
import time
import threading
# -----------------------
# Umgebungseinstellungen
# -----------------------
# Fenstergröße anpassen
window.size = (960, 640) # Ganzzahlige Werte verwenden
window.borderless = False
ENV_SIZE = (50, 50, 20) # Größe der Umgebung (x, y, z)
VOXEL_SIZE = 1
# Ameiseneinstellungen
INITIAL_WORKERS = 50
INITIAL_SOLDIERS = 10
ANT_SPEED = 0.1
ANT_LIFESPAN = 5000
# Pheromon-Einstellungen
FOOD_PHEROMONE = 0
HOME_PHEROMONE = 1
DANGER_PHEROMONE = 2
MARKER_PHEROMONE = 3
PHEROMONE_TYPES = 4
PHEROMONE_EVAPORATION_RATE = 0.01
PHEROMONE_DIFFUSION_RATE = 0.1
PHEROMONE_DEPOSIT_AMOUNT = 10.0
PHEROMONE_MAX = 100.0
PHEROMONE_THRESHOLD = 1.0
# Voxel-Typen
AIR = 0
SOIL = 1
FOOD = 2
NEST = 3
STONE = 4
PLANT = 5
DEBRIS = 6
NEST_MATERIAL = 7
EXCRETION = 8
LARVA = 9
PUPA = 10
# Darstellungseinstellungen
ANT_COLORS = {
'WORKER': color.rgb(139, 69, 19),
'SOLDIER': color.rgb(165, 42, 42),
'QUEEN': color.rgb(184, 134, 11),
'LARVA': color.rgb(255, 255, 224),
'PUPA': color.rgb(245, 245, 220)
}
# Simulationseinstellungen
DAY_LENGTH = 1000
current_time_step = 0
FOOD_SPAWN_RATE = 0.005
# Tageszyklus
TIME_OF_DAY = 0 # Startzeit
TIME_SPEED = 0.1 # Geschwindigkeit des Zeitfortschritts
IS_DAY = True
# -----------------------
# Klassen
# -----------------------
class Voxel(Entity):
def __init__(self, position=(0, 0, 0), voxel_type=AIR, carried=False):
super().__init__(
parent=scene,
position=position,
model='cube' if voxel_type != AIR else None,
color=self.get_color(voxel_type),
scale=VOXEL_SIZE,
collider='box' if voxel_type != AIR else None,
visible=not carried
)
self.type = voxel_type
self.carried = carried
self.pheromones = np.zeros(PHEROMONE_TYPES)
self.update_interval = random.uniform(0.1, 0.5)
self.last_update = time.time()
def get_color(self, voxel_type):
colors = {
SOIL: color.rgb(139, 69, 19),
FOOD: color.yellow,
NEST: color.rgb(255, 165, 0),
STONE: color.gray,
PLANT: color.green,
DEBRIS: color.rgb(210, 180, 140),
NEST_MATERIAL: color.rgb(160, 82, 45),
EXCRETION: color.rgb(105, 105, 105),
LARVA: ANT_COLORS['LARVA'],
PUPA: ANT_COLORS['PUPA'],
AIR: color.clear
}
return colors.get(voxel_type, color.clear)
def evaporate_pheromones(self):
self.pheromones *= (1 - PHEROMONE_EVAPORATION_RATE)
self.pheromones = np.clip(self.pheromones, 0, PHEROMONE_MAX)
def diffuse_pheromones(self, neighbors):
for pheromone_type in range(len(self.pheromones)):
total_pheromone = self.pheromones[pheromone_type] * (1 - PHEROMONE_DIFFUSION_RATE)
diffusion_amount = self.pheromones[pheromone_type] * PHEROMONE_DIFFUSION_RATE
total_neighbors = len(neighbors)
if total_neighbors > 0:
diffusion_per_neighbor = diffusion_amount / total_neighbors
for neighbor in neighbors:
neighbor.pheromones[pheromone_type] += diffusion_per_neighbor
self.pheromones[pheromone_type] = total_pheromone
def is_supported(self, environment):
x, y, z = int(self.position.x), int(self.position.y), int(self.position.z)
if z == 0:
return True
below_voxel = environment.get_voxel(x, y, z - 1)
if below_voxel and below_voxel.type != AIR:
return True
for dx in [-1, 0, 1]:
nx = x + dx
if 0 <= nx < ENV_SIZE[0]:
for dy in [-1, 0, 1]:
ny = y + dy
if 0 <= ny < ENV_SIZE[1]:
neighbor_voxel = environment.get_voxel(nx, ny, z - 1)
if neighbor_voxel and neighbor_voxel.type != AIR:
return True
return False
def update_pheromones(self, environment):
current_time = time.time()
if current_time - self.last_update >= self.update_interval:
neighbors = environment.get_neighbors(int(self.position.x), int(self.position.y), int(self.position.z))
self.diffuse_pheromones(neighbors)
self.evaporate_pheromones()
self.last_update = current_time
class Environment:
def __init__(self):
self.simplex = OpenSimplex(seed=random.randint(0, 10000))
self.grid = np.empty(ENV_SIZE, dtype=object)
self.initialize_environment()
def initialize_environment(self):
for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
noise_value = self.simplex.noise2(x / 10, y / 10)
height = int((noise_value + 1) * (ENV_SIZE[2] // 2))
for z in range(ENV_SIZE[2]):
voxel_type = SOIL if z < height else AIR
self.grid[x][y][z] = Voxel(position=(x, y, z), voxel_type=voxel_type)
for _ in range(100):
x = random.randint(0, ENV_SIZE[0] - 1)
y = random.randint(0, ENV_SIZE[1] - 1)
z = self.get_surface_z(x, y)
if z < ENV_SIZE[2]:
voxel_type = STONE if random.random() < 0.5 else PLANT
self.grid[x][y][z] = Voxel(position=(x, y, z), voxel_type=voxel_type)
# Nest positionieren
nest_x, nest_y = ENV_SIZE[0] // 2, ENV_SIZE[1] // 2
nest_z = self.get_surface_z(nest_x, nest_y)
if nest_z < ENV_SIZE[2]:
self.grid[nest_x][nest_y][nest_z] = Voxel(position=(nest_x, nest_y, nest_z), voxel_type=NEST)
def get_surface_z(self, x, y):
for z in range(ENV_SIZE[2] - 1, -1, -1):
if self.grid[x][y][z].type != AIR:
return z + 1 if z + 1 < ENV_SIZE[2] else z
return 0
def get_voxel(self, x, y, z):
if 0 <= x < ENV_SIZE[0] and 0 <= y < ENV_SIZE[1] and 0 <= z < ENV_SIZE[2]:
return self.grid[x][y][z]
return None
def update_pheromones(self):
for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
for z in range(ENV_SIZE[2]):
voxel = self.grid[x][y][z]
if voxel and voxel.type == AIR:
voxel.update_pheromones(self)
def get_neighbors(self, x, y, z):
neighbors = []
for dx in [-1, 0, 1]:
nx = x + dx
if 0 <= nx < ENV_SIZE[0]:
for dy in [-1, 0, 1]:
ny = y + dy
if 0 <= ny < ENV_SIZE[1]:
for dz in [-1, 0, 1]:
nz = z + dz
if 0 <= nz < ENV_SIZE[2]:
if dx == 0 and dy == 0 and dz == 0:
continue
neighbor = self.grid[nx][ny][nz]
if neighbor.type == AIR:
neighbors.append(neighbor)
return neighbors
def spawn_food(self):
if random.random() < FOOD_SPAWN_RATE:
x = random.randint(0, ENV_SIZE[0] - 1)
y = random.randint(0, ENV_SIZE[1] - 1)
z = self.get_surface_z(x, y)
if z < ENV_SIZE[2] and self.grid[x][y][z].type == AIR:
self.grid[x][y][z] = Voxel(position=(x, y, z), voxel_type=FOOD)
def apply_physics(self):
for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
for z in range(ENV_SIZE[2]-1, -1, -1):
voxel = self.grid[x][y][z]
if voxel.type != AIR and not voxel.carried and not voxel.is_supported(self):
new_z = z - 1
while new_z >= 0 and self.grid[x][y][new_z].type == AIR:
new_z -= 1
new_z += 1
if new_z != z:
self.grid[x][y][new_z] = voxel
voxel.position = (x, y, new_z)
self.grid[x][y][z] = Voxel(position=(x, y, z), voxel_type=AIR)
class AntSensorySystem:
def __init__(self):
self.range = 1
def perceive(self, ant):
pheromone_levels = np.zeros(PHEROMONE_TYPES)
x, y, z = int(ant.position.x), int(ant.position.y), int(ant.position.z)
for dx in range(-self.range, self.range + 1):
nx = x + dx
if 0 <= nx < ENV_SIZE[0]:
for dy in range(-self.range, self.range + 1):
ny = y + dy
if 0 <= ny < ENV_SIZE[1]:
for dz in [-1, 0, 1]:
nz = z + dz
if 0 <= nz < ENV_SIZE[2]:
neighbor = ant.environment.get_voxel(nx, ny, nz)
if neighbor:
pheromone_levels += neighbor.pheromones
return pheromone_levels
class Leg(Entity):
def __init__(self, ant, index):
super().__init__(
parent=ant,
model=Mesh(mode='line', thickness=2),
color=color.black
)
self.ant = ant
self.index = index
self.max_length = 0.3
self.swing_phase = random.uniform(0, math.pi * 2)
def get_start_point(self):
angle = (self.index / 6) * 360
rad = math.radians(angle)
x_offset = math.cos(rad) * 0.1
y_offset = math.sin(rad) * 0.1
return Vec3(x_offset, y_offset, -0.1)
def calculate_end_pos(self):
angle = (self.index / 6) * 360 + self.ant.rotation_y
rad = math.radians(angle)
x_offset = math.cos(rad) * self.max_length
y_offset = math.sin(rad) * self.max_length
leg_end_world = self.ant.world_position + Vec3(x_offset, y_offset, -0.2)
swing = math.sin(time.time() * 5 + self.swing_phase) * 0.1
leg_end_world += Vec3(0, 0, swing)
hit_info = raycast(leg_end_world, Vec3(0, 0, -1), distance=self.max_length * 2, ignore=(self.ant,))
if hit_info.hit:
return self.ant.world_to_local(hit_info.world_point)
else:
return self.ant.world_to_local(leg_end_world)
def update_leg(self):
start = self.get_start_point()
end = self.calculate_end_pos()
self.model.vertices = [start, end]
self.model.generate()
class Ant(Entity):
def __init__(self, position, colony, environment, caste='WORKER'):
super().__init__(
parent=scene,
position=position,
model=None,
color=ANT_COLORS[caste],
scale=0.2
)
self.caste = caste
self.colony = colony
self.environment = environment
self.has_food = False
self.carrying_material = None # Gehaltener Voxel
self.energy = 100
self.age = 0
self.max_age = ANT_LIFESPAN
self.direction = self.random_direction()
self.state = 'FORAGING'
self.sensory_system = AntSensorySystem()
self.legs = [Leg(self, i) for i in range(6)] # 6 Beine
self.generate_body()
self.activity_threshold = random.uniform(0.2, 0.8) # Für Tageszyklus
def generate_body(self):
self.head = Entity(parent=self, model='sphere', color=self.color,
scale=Vec3(0.15, 0.15, 0.2), position=Vec3(0, 0.1, 0.1))
self.thorax = Entity(parent=self, model='sphere', color=self.color,
scale=Vec3(0.2, 0.2, 0.25), position=Vec3(0, 0, 0))
self.abdomen = Entity(parent=self, model='sphere', color=self.color,
scale=Vec3(0.25, 0.25, 0.3), position=Vec3(0, -0.2, -0.1))
def random_direction(self):
angle = random.uniform(0, 360)
return Vec3(math.cos(math.radians(angle)), math.sin(math.radians(angle)), 0)
def perceive(self):
return self.sensory_system.perceive(self)
def decide(self):
if IS_DAY or random.random() < self.activity_threshold:
if self.state == 'FORAGING':
self.decide_foraging()
elif self.state == 'DEPOSITING':
self.decide_depositing()
elif self.state == 'CARING':
self.decide_caring()
else:
self.direction = Vec3(0, 0, 0) # Bleibt inaktiv
def decide_foraging(self):
pheromones = self.perceive()
if self.has_food or self.carrying_material:
if pheromones[HOME_PHEROMONE] > PHEROMONE_THRESHOLD and random.random() < 0.9:
self.follow_pheromone(HOME_PHEROMONE)
else:
self.direction = self.random_direction()
else:
if pheromones[FOOD_PHEROMONE] > PHEROMONE_THRESHOLD and random.random() < 0.9:
self.follow_pheromone(FOOD_PHEROMONE)
else:
self.direction = self.random_direction()
def decide_depositing(self):
x, y, z = int(self.position.x), int(self.position.y), int(self.position.z)
voxel = self.environment.get_voxel(x, y, z)
if self.has_food and voxel and (voxel.type == NEST or voxel.type == NEST_MATERIAL):
self.has_food = False
self.colony.food_storage += 1
self.energy += 50
if random.random() < 0.5:
excretion_voxel = Voxel(position=(x, y, z), voxel_type=EXCRETION)
self.environment.grid[x][y][z] = excretion_voxel
self.state = 'CARING'
elif self.carrying_material and voxel and (voxel.type == NEST or voxel.type == NEST_MATERIAL):
self.build_nest()
self.state = 'FORAGING'
else:
self.follow_pheromone(HOME_PHEROMONE)
def decide_caring(self):
if random.random() < 0.5:
self.care_larvae()
else:
self.state = 'FORAGING'
def care_larvae(self):
x, y, z = int(self.position.x), int(self.position.y), int(self.position.z)
for dx in [-1, 0, 1]:
nx = x + dx
if 0 <= nx < ENV_SIZE[0]:
for dy in [-1, 0, 1]:
ny = y + dy
if 0 <= ny < ENV_SIZE[1]:
nz = z
voxel = self.environment.get_voxel(nx, ny, nz)
if voxel and (voxel.type == LARVA or voxel.type == PUPA):
self.deposit_pheromones() # Hinterlässt Pheromon
self.state = 'FORAGING'
return
self.direction = self.random_direction()
def follow_pheromone(self, pheromone_type):
x, y, z = int(self.position.x), int(self.position.y), int(self.position.z)
max_pheromone = -1
best_direction = None
for dx in [-1, 0, 1]:
nx = x + dx
if 0 <= nx < ENV_SIZE[0]:
for dy in [-1, 0, 1]:
ny = y + dy
if 0 <= ny < ENV_SIZE[1]:
nz = z
neighbor = self.environment.get_voxel(nx, ny, nz)
if neighbor and neighbor.type == AIR:
pheromone_level = neighbor.pheromones[pheromone_type]
if pheromone_level > max_pheromone:
max_pheromone = pheromone_level
best_direction = Vec3(dx, dy, 0)
if best_direction:
self.direction = best_direction.normalized() + Vec3(random.uniform(-0.1, 0.1),
random.uniform(-0.1, 0.1),
0)
else:
self.direction = self.random_direction()
def move(self):
if self.direction.length() == 0:
return # Keine Bewegung
self.direction = self.direction.normalized()
new_position = self.position + self.direction * ANT_SPEED
x, y, z = int(new_position.x), int(new_position.y), int(new_position.z)
if 0 <= x < ENV_SIZE[0] and 0 <= y < ENV_SIZE[1] and 0 <= z < ENV_SIZE[2]:
voxel = self.environment.get_voxel(x, y, z)
if voxel and voxel.type == AIR:
self.position = new_position
self.energy -= 0.1
else:
self.direction = self.random_direction()
else:
self.direction = self.random_direction()
if self.carrying_material:
self.carrying_material.position = self.position + Vec3(0, 0, 0.2)
def act(self):
if self.state == 'FORAGING':
self.search_for_resources()
elif self.state == 'DEPOSITING':
self.decide_depositing()
elif self.state == 'CARING':
self.decide_caring()
def search_for_resources(self):
x, y, z = int(self.position.x), int(self.position.y), int(self.position.z)
voxel = self.environment.get_voxel(x, y, z)
if not self.has_food and not self.carrying_material:
if voxel and voxel.type == FOOD:
self.has_food = True
self.environment.grid[x][y][z] = Voxel(position=(x, y, z), voxel_type=AIR)
self.state = 'DEPOSITING'
elif voxel and voxel.type == SOIL and random.random() < 0.1:
self.carrying_material = Voxel(position=self.position + Vec3(0, 0, 0.2), voxel_type=DEBRIS, carried=True)
self.carrying_material.parent = self
self.environment.grid[x][y][z] = Voxel(position=(x, y, z), voxel_type=AIR)
self.state = 'DEPOSITING'
def deposit_pheromones(self):
x, y, z = int(self.position.x), int(self.position.y), int(self.position.z)
voxel = self.environment.get_voxel(x, y, z)
if voxel:
if self.has_food:
voxel.pheromones[HOME_PHEROMONE] += PHEROMONE_DEPOSIT_AMOUNT
else:
voxel.pheromones[FOOD_PHEROMONE] += PHEROMONE_DEPOSIT_AMOUNT
def build_nest(self):
x, y, z = int(self.position.x), int(self.position.y), int(self.position.z)
best_nx, best_ny, best_nz = -1, -1, -1
max_neighbors = -1
for dx in [-1, 0, 1]:
nx = x + dx
if 0 <= nx < ENV_SIZE[0]:
for dy in [-1, 0, 1]:
ny = y + dy
if 0 <= ny < ENV_SIZE[1]:
for dz in [-1, 0, 1]:
nz = z + dz
if 0 <= nz < ENV_SIZE[2]:
neighbor = self.environment.get_voxel(nx, ny, nz)
if neighbor and neighbor.type == AIR:
neighbors = self.count_neighbors(nx, ny, nz, NEST_MATERIAL)
if neighbors > max_neighbors:
max_neighbors = neighbors
best_nx, best_ny, best_nz = nx, ny, nz
if best_nx != -1:
self.environment.grid[best_nx][best_ny][best_nz] = Voxel(position=(best_nx, best_ny, best_nz), voxel_type=NEST_MATERIAL)
destroy(self.carrying_material)
self.carrying_material = None
def count_neighbors(self, x, y, z, voxel_type):
count = 0
for dx in [-1, 0, 1]:
nx = x + dx
if 0 <= nx < ENV_SIZE[0]:
for dy in [-1, 0, 1]:
ny = y + dy
if 0 <= ny < ENV_SIZE[1]:
for dz in [-1, 0, 1]:
nz = z + dz
if 0 <= nz < ENV_SIZE[2]:
neighbor = self.environment.get_voxel(nx, ny, nz)
if neighbor and neighbor.type == voxel_type:
count += 1
return count
def update(self):
self.age += 1
if self.age > self.max_age or self.energy <= 0:
self.die()
return
self.decide()
self.move()
self.act()
self.deposit_pheromones()
for leg in self.legs:
leg.update_leg()
def die(self):
if self.carrying_material:
self.carrying_material.visible = True
self.carrying_material.carried = False
self.carrying_material.parent = scene
self.environment.grid[int(self.position.x)][int(self.position.y)][int(self.position.z)] = self.carrying_material
self.carrying_material = None
self.colony.remove_ant(self)
destroy(self)
class QueenAnt(Entity):
def __init__(self, position, colony):
super().__init__(
parent=scene,
position=position,
model='sphere',
color=ANT_COLORS['QUEEN'],
scale=0.5
)
self.colony = colony
self.laying_timer = 500
def update(self):
self.laying_timer -= 1
if self.laying_timer <= 0:
if self.colony.food_storage >= 5:
self.colony.food_storage -= 5
self.lay_egg()
self.laying_timer = 500
def lay_egg(self):
larva = Larva(position=self.position, colony=self.colony)
self.colony.add_larva(larva)
class Larva(Entity):
def __init__(self, position, colony):
super().__init__(
parent=scene,
position=position,
model='sphere',
color=ANT_COLORS['LARVA'],
scale=0.1
)
self.colony = colony
self.growth_time = 1000
def update(self):
self.growth_time -= 1
if self.growth_time <= 0:
self.pupate()
def pupate(self):
pupa = Pupa(position=self.position, colony=self.colony)
self.colony.add_pupa(pupa)
destroy(self)
self.colony.larvae.remove(self)
class Pupa(Entity):
def __init__(self, position, colony):
super().__init__(
parent=scene,
position=position,
model='sphere',
color=ANT_COLORS['PUPA'],
scale=0.1
)
self.colony = colony
self.hatching_time = 1000
def update(self):
self.hatching_time -= 1
if self.hatching_time <= 0:
self.hatch()
def hatch(self):
ant = Ant(position=self.position, colony=self.colony, environment=self.colony.environment)
self.colony.add_ant(ant)
destroy(self)
self.colony.pupae.remove(self)
class Colony:
def __init__(self, environment):
self.environment = environment
self.ants = []
self.larvae = []
self.pupae = []
self.food_storage = 0
nest_x, nest_y = ENV_SIZE[0] // 2, ENV_SIZE[1] // 2
nest_z = environment.get_surface_z(nest_x, nest_y)
self.nest_position = Vec3(nest_x, nest_y, nest_z)
self.queen = QueenAnt(position=self.nest_position, colony=self)
def add_ant(self, ant):
self.ants.append(ant)
def remove_ant(self, ant):
if ant in self.ants:
self.ants.remove(ant)
def add_larva(self, larva):
self.larvae.append(larva)
def add_pupa(self, pupa):
self.pupae.append(pupa)
def update(self):
self.queen.update()
for larva in self.larvae[:]:
larva.update()
for pupa in self.pupae[:]:
pupa.update()
class CameraController(Entity):
def __init__(self):
super().__init__()
self.camera_pivot = Entity()
camera.parent = self.camera_pivot
camera.position = (0, -30, 20)
camera.rotation_x = 30
self.rotation_speed = 100
self.zoom_speed = 20
self.current_zoom = 30
self.focus_position = Vec3(ENV_SIZE[0] // 2, ENV_SIZE[1] // 2, ENV_SIZE[2] // 2)
self.camera_pivot.position = self.focus_position
self.following = False
self.follow_target = None
def update(self):
dt = time.dt
if mouse.left and mouse.left_double_click:
hit_entity = mouse.hovered_entity
if isinstance(hit_entity, Voxel) or isinstance(hit_entity, Ant):
self.focus_position = hit_entity.position
self.follow_target = hit_entity if isinstance(hit_entity, Ant) else None
self.following = isinstance(hit_entity, Ant)
if held_keys['right mouse']:
delta = Vec2(mouse.velocity[0], mouse.velocity[1])
self.camera_pivot.rotation_y += delta.x * dt * self.rotation_speed
self.camera_pivot.rotation_x -= delta.y * dt * self.rotation_speed
self.camera_pivot.rotation_x = max(-90, min(90, self.camera_pivot.rotation_x))
self.current_zoom -= mouse.scroll_y * self.zoom_speed * dt
self.current_zoom = max(5, min(100, self.current_zoom))
camera.position = Vec3(0, -self.current_zoom, self.current_zoom * 0.5)
if self.following and self.follow_target:
self.focus_position = self.follow_target.position
self.camera_pivot.position = lerp(self.camera_pivot.position, self.focus_position, 0.1)
# -----------------------
# Hilfsfunktionen
# -----------------------
def update_time_of_day():
global TIME_OF_DAY, IS_DAY
TIME_OF_DAY += TIME_SPEED
if TIME_OF_DAY >= 24:
TIME_OF_DAY = 0
IS_DAY = 6 <= TIME_OF_DAY <= 18 # Tag von 6 bis 18 Uhr
def load_environment_and_colony(progress_bar):
environment = Environment()
colony = Colony(environment)
progress_bar.text = "Kolonie wird initialisiert..."
# Anfangs-Ameisen erstellen
for _ in range(INITIAL_WORKERS):
offset = Vec3(random.uniform(-5, 5), random.uniform(-5, 5), 0)
position = colony.nest_position + offset
position.x = max(0, min(position.x, ENV_SIZE[0] - 1))
position.y = max(0, min(position.y, ENV_SIZE[1] - 1))
position.z = environment.get_surface_z(int(position.x), int(position.y))
ant = Ant(position=position, colony=colony, environment=environment)
colony.add_ant(ant)
# Soldatameisen erstellen
for _ in range(INITIAL_SOLDIERS):
offset = Vec3(random.uniform(-5, 5), random.uniform(-5, 5), 0)
position = colony.nest_position + offset
position.x = max(0, min(position.x, ENV_SIZE[0] - 1))
position.y = max(0, min(position.y, ENV_SIZE[1] - 1))
position.z = environment.get_surface_z(int(position.x), int(position.y))
ant = Ant(position=position, colony=colony, environment=environment, caste='SOLDIER')
colony.add_ant(ant)
progress_bar.value = 100 # Set progress to complete
return environment, colony
def main():
app = Ursina()
# Fortschrittsbalken erstellen
progress_bar = ProgressBar(max=100, position=(0, 0.3), scale=(0.5, 0.05), color=color.green)
progress_text = Text(text="Lade...", position=(0, 0.4), origin=(0, 0), scale=2)
environment, colony = None, None
def load_in_background():
nonlocal environment, colony
environment, colony = load_environment_and_colony(progress_bar)
progress_text.text = "Bereit!"
threading.Thread(target=load_in_background).start()
# UI-Text für gesammeltes Essen und Tageszeit
food_text = Text(text='', position=window.top_left, origin=(0, 0), background=True)
time_text = Text(text='', position=window.top_right, origin=(1, 0), background=True)
def update():
if progress_bar.value < 100:
return # Nicht aktualisieren, wenn der Ladevorgang noch läuft
global current_time_step
current_time_step += 1
if environment and colony: # Prüfen ob sie geladen sind
environment.update_pheromones()
environment.spawn_food()
environment.apply_physics()
update_time_of_day()
# Ameisen aktualisieren
for ant in colony.ants:
ant.update()
# Kolonie aktualisieren
colony.update()
# UI aktualisieren
food_text.text = f"Essen gesammelt: {colony.food_storage}"
time_text.text = f"Zeit: {TIME_OF_DAY:.2f} Uhr ({'Tag' if IS_DAY else 'Nacht'})"
app.run()
if __name__ == "__main__":
main()
import random
import numpy as np
from ursina import *
from opensimplex import OpenSimplex
import math
import time
import threading
# -----------------------
# Umgebungseinstellungen
# -----------------------
# Fenstergröße anpassen
window.size = (960, 640) # Ganzzahlige Werte verwenden
window.borderless = False
ENV_SIZE = (50, 50, 20) # Größe der Umgebung (x, y, z)
VOXEL_SIZE = 1
# Ameiseneinstellungen
INITIAL_WORKERS = 50
INITIAL_SOLDIERS = 10
ANT_SPEED = 0.1
ANT_LIFESPAN = 5000
# Pheromon-Einstellungen
FOOD_PHEROMONE = 0
HOME_PHEROMONE = 1
DANGER_PHEROMONE = 2
MARKER_PHEROMONE = 3
PHEROMONE_TYPES = 4
PHEROMONE_EVAPORATION_RATE = 0.01
PHEROMONE_DIFFUSION_RATE = 0.1
PHEROMONE_DEPOSIT_AMOUNT = 10.0
PHEROMONE_MAX = 100.0
PHEROMONE_THRESHOLD = 1.0
# Voxel-Typen
AIR = 0
SOIL = 1
FOOD = 2
NEST = 3
STONE = 4
PLANT = 5
DEBRIS = 6
NEST_MATERIAL = 7
EXCRETION = 8
LARVA = 9
PUPA = 10
# Darstellungseinstellungen
ANT_COLORS = {
'WORKER': color.rgb(139, 69, 19),
'SOLDIER': color.rgb(165, 42, 42),
'QUEEN': color.rgb(184, 134, 11),
'LARVA': color.rgb(255, 255, 224),
'PUPA': color.rgb(245, 245, 220)
}
# Simulationseinstellungen
DAY_LENGTH = 1000
current_time_step = 0
FOOD_SPAWN_RATE = 0.005
# Tageszyklus
TIME_OF_DAY = 0 # Startzeit
TIME_SPEED = 0.1 # Geschwindigkeit des Zeitfortschritts
IS_DAY = True
# -----------------------
# Klassen
# -----------------------
class Voxel(Entity):
def __init__(self, position=(0, 0, 0), voxel_type=AIR, carried=False):
super().__init__(
parent=scene,
position=position,
model='cube' if voxel_type != AIR else None,
color=self.get_color(voxel_type),
scale=VOXEL_SIZE,
collider='box' if voxel_type != AIR else None,
visible=not carried
)
self.type = voxel_type
self.carried = carried
self.pheromones = np.zeros(PHEROMONE_TYPES)
self.update_interval = random.uniform(0.1, 0.5)
self.last_update = time.time()
self.neighbor_positions = self.get_neighbor_positions()
def get_color(self, voxel_type):
colors = {
SOIL: color.rgb(139, 69, 19),
FOOD: color.yellow,
NEST: color.rgb(255, 165, 0),
STONE: color.gray,
PLANT: color.green,
DEBRIS: color.rgb(210, 180, 140),
NEST_MATERIAL: color.rgb(160, 82, 45),
EXCRETION: color.rgb(105, 105, 105),
LARVA: ANT_COLORS['LARVA'],
PUPA: ANT_COLORS['PUPA'],
AIR: color.clear
}
return colors.get(voxel_type, color.clear)
def evaporate_pheromones(self):
self.pheromones *= (1 - PHEROMONE_EVAPORATION_RATE)
self.pheromones = np.clip(self.pheromones, 0, PHEROMONE_MAX)
def diffuse_pheromones(self, neighbors):
for pheromone_type in range(len(self.pheromones)):
total_pheromone = self.pheromones[pheromone_type] * (1 - PHEROMONE_DIFFUSION_RATE)
diffusion_amount = self.pheromones[pheromone_type] * PHEROMONE_DIFFUSION_RATE
total_neighbors = len(neighbors)
if total_neighbors > 0:
diffusion_per_neighbor = diffusion_amount / total_neighbors
for neighbor in neighbors:
neighbor.pheromones[pheromone_type] += diffusion_per_neighbor
self.pheromones[pheromone_type] = total_pheromone
def is_supported(self, environment):
x, y, z = int(self.position.x), int(self.position.y), int(self.position.z)
if z == 0:
return True
below_voxel = environment.get_voxel(x, y, z - 1)
if below_voxel and below_voxel.type != AIR:
return True
for dx in [-1, 0, 1]:
nx = x + dx
if 0 <= nx < ENV_SIZE[0]:
for dy in [-1, 0, 1]:
ny = y + dy
if 0 <= ny < ENV_SIZE[1]:
neighbor_voxel = environment.get_voxel(nx, ny, z - 1)
if neighbor_voxel and neighbor_voxel.type != AIR:
return True
return False
def get_neighbor_positions(self):
neighbor_positions = []
for dx in [-1, 0, 1]:
nx = int(self.position.x) + dx
if 0 <= nx < ENV_SIZE[0]:
for dy in [-1, 0, 1]:
ny = int(self.position.y) + dy
if 0 <= ny < ENV_SIZE[1]:
for dz in [-1, 0, 1]:
nz = int(self.position.z) + dz
if 0 <= nz < ENV_SIZE[2]:
if dx == 0 and dy == 0 and dz == 0:
continue
neighbor_positions.append((nx, ny, nz))
return neighbor_positions
def update_pheromones(self, environment):
current_time = time.time()
if current_time - self.last_update >= self.update_interval:
neighbors = []
for pos in self.neighbor_positions:
neighbor = environment.get_voxel(*pos)
if neighbor and neighbor.type == AIR:
neighbors.append(neighbor)
self.diffuse_pheromones(neighbors)
self.evaporate_pheromones()
self.last_update = current_time
class Environment:
def __init__(self):
self.simplex = OpenSimplex(seed=random.randint(0, 10000))
self.grid = np.empty(ENV_SIZE, dtype=object)
self.initialize_environment()
def initialize_environment(self):
for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
noise_value = self.simplex.noise2(x / 10, y / 10)
height = int((noise_value + 1) * (ENV_SIZE[2] // 2))
for z in range(ENV_SIZE[2]):
voxel_type = SOIL if z < height else AIR
self.grid[x][y][z] = Voxel(position=(x, y, z), voxel_type=voxel_type)
for _ in range(100):
x = random.randint(0, ENV_SIZE[0] - 1)
y = random.randint(0, ENV_SIZE[1] - 1)
z = self.get_surface_z(x, y)
if z < ENV_SIZE[2]:
voxel_type = STONE if random.random() < 0.5 else PLANT
self.grid[x][y][z] = Voxel(position=(x, y, z), voxel_type=voxel_type)
# Nest positionieren
nest_x, nest_y = ENV_SIZE[0] // 2, ENV_SIZE[1] // 2
nest_z = self.get_surface_z(nest_x, nest_y)
if nest_z < ENV_SIZE[2]:
self.grid[nest_x][nest_y][nest_z] = Voxel(position=(nest_x, nest_y, nest_z), voxel_type=NEST)
def get_surface_z(self, x, y):
for z in range(ENV_SIZE[2] - 1, -1, -1):
if self.grid[x][y][z].type != AIR:
return z + 1 if z + 1 < ENV_SIZE[2] else z
return 0
def get_voxel(self, x, y, z):
if 0 <= x < ENV_SIZE[0] and 0 <= y < ENV_SIZE[1] and 0 <= z < ENV_SIZE[2]:
return self.grid[x][y][z]
return None
def update_pheromones(self):
for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
for z in range(ENV_SIZE[2]):
voxel = self.grid[x][y][z]
if voxel and voxel.type == AIR:
voxel.update_pheromones(self)
def get_neighbors(self, x, y, z):
neighbors = []
for dx in [-1, 0, 1]:
nx = x + dx
if 0 <= nx < ENV_SIZE[0]:
for dy in [-1, 0, 1]:
ny = y + dy
if 0 <= ny < ENV_SIZE[1]:
for dz in [-1, 0, 1]:
nz = z + dz
if 0 <= nz < ENV_SIZE[2]:
if dx == 0 and dy == 0 and dz == 0:
continue
neighbor = self.grid[nx][ny][nz]
if neighbor and neighbor.type == AIR:
neighbors.append(neighbor)
return neighbors
def spawn_food(self):
if random.random() < FOOD_SPAWN_RATE:
x = random.randint(0, ENV_SIZE[0] - 1)
y = random.randint(0, ENV_SIZE[1] - 1)
z = self.get_surface_z(x, y)
if z < ENV_SIZE[2] and self.grid[x][y][z].type == AIR:
self.grid[x][y][z] = Voxel(position=(x, y, z), voxel_type=FOOD)
def apply_physics(self):
for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
for z in range(ENV_SIZE[2]-1, -1, -1):
voxel = self.grid[x][y][z]
if voxel.type != AIR and not voxel.carried and not voxel.is_supported(self):
new_z = z - 1
while new_z >= 0 and self.grid[x][y][new_z].type == AIR:
new_z -= 1
new_z += 1
if new_z != z:
self.grid[x][y][new_z] = voxel
voxel.position = (x, y, new_z)
self.grid[x][y][z] = Voxel(position=(x, y, z), voxel_type=AIR)
class AntSensorySystem:
def __init__(self):
self.range = 1
def perceive(self, ant):
pheromone_levels = np.zeros(PHEROMONE_TYPES)
x, y, z = int(ant.position.x), int(ant.position.y), int(ant.position.z)
for dx in range(-self.range, self.range + 1):
nx = x + dx
if 0 <= nx < ENV_SIZE[0]:
for dy in range(-self.range, self.range + 1):
ny = y + dy
if 0 <= ny < ENV_SIZE[1]:
for dz in [-1, 0, 1]:
nz = z + dz
if 0 <= nz < ENV_SIZE[2]:
neighbor = ant.environment.get_voxel(nx, ny, nz)
if neighbor:
pheromone_levels += neighbor.pheromones
return pheromone_levels
class Leg(Entity):
def __init__(self, ant, index):
super().__init__(
parent=ant,
model=Mesh(mode='line', thickness=2),
color=color.black
)
self.ant = ant
self.index = index
self.max_length = 0.3
self.swing_phase = random.uniform(0, math.pi * 2)
def get_start_point(self):
angle = (self.index / 6) * 360
rad = math.radians(angle)
x_offset = math.cos(rad) * 0.1
y_offset = math.sin(rad) * 0.1
return Vec3(x_offset, y_offset, -0.1)
def calculate_end_pos(self):
angle = (self.index / 6) * 360 + self.ant.rotation_y
rad = math.radians(angle)
x_offset = math.cos(rad) * self.max_length
y_offset = math.sin(rad) * self.max_length
leg_end_world = self.ant.world_position + Vec3(x_offset, y_offset, -0.2)
swing = math.sin(time.time() * 5 + self.swing_phase) * 0.1
leg_end_world += Vec3(0, 0, swing)
hit_info = raycast(leg_end_world, Vec3(0, 0, -1), distance=self.max_length * 2, ignore=(self.ant,))
if hit_info.hit:
return self.ant.world_to_local(hit_info.world_point)
else:
return self.ant.world_to_local(leg_end_world)
def update_leg(self):
start = self.get_start_point()
end = self.calculate_end_pos()
self.model.vertices = [start, end]
self.model.generate()
class Ant(Entity):
def __init__(self, position, colony, environment, caste='WORKER'):
super().__init__(
parent=scene,
position=position,
model=None,
color=ANT_COLORS[caste],
scale=0.2
)
self.caste = caste
self.colony = colony
self.environment = environment
self.has_food = False
self.carrying_material = None # Gehaltener Voxel
self.energy = 100
self.age = 0
self.max_age = ANT_LIFESPAN
self.direction = self.random_direction()
self.state = 'FORAGING'
self.sensory_system = AntSensorySystem()
self.legs = [Leg(self, i) for i in range(6)] # 6 Beine
self.generate_body()
self.activity_threshold = random.uniform(0.2, 0.8) # Für Tageszyklus
def generate_body(self):
self.head = Entity(parent=self, model='sphere', color=self.color,
scale=Vec3(0.15, 0.15, 0.2), position=Vec3(0, 0.1, 0.1))
self.thorax = Entity(parent=self, model='sphere', color=self.color,
scale=Vec3(0.2, 0.2, 0.25), position=Vec3(0, 0, 0))
self.abdomen = Entity(parent=self, model='sphere', color=self.color,
scale=Vec3(0.25, 0.25, 0.3), position=Vec3(0, -0.2, -0.1))
def random_direction(self):
angle = random.uniform(0, 360)
return Vec3(math.cos(math.radians(angle)), math.sin(math.radians(angle)), 0)
def perceive(self):
return self.sensory_system.perceive(self)
def decide(self):
if IS_DAY or random.random() < self.activity_threshold:
if self.state == 'FORAGING':
self.decide_foraging()
elif self.state == 'DEPOSITING':
self.decide_depositing()
elif self.state == 'CARING':
self.decide_caring()
else:
self.direction = Vec3(0, 0, 0) # Bleibt inaktiv
def decide_foraging(self):
pheromones = self.perceive()
if self.has_food or self.carrying_material:
if pheromones[HOME_PHEROMONE] > PHEROMONE_THRESHOLD and random.random() < 0.9:
self.follow_pheromone(HOME_PHEROMONE)
else:
self.direction = self.random_direction()
else:
if pheromones[FOOD_PHEROMONE] > PHEROMONE_THRESHOLD and random.random() < 0.9:
self.follow_pheromone(FOOD_PHEROMONE)
else:
self.direction = self.random_direction()
def decide_depositing(self):
x, y, z = int(self.position.x), int(self.position.y), int(self.position.z)
voxel = self.environment.get_voxel(x, y, z)
if self.has_food and voxel and (voxel.type == NEST or voxel.type == NEST_MATERIAL):
self.has_food = False
self.colony.food_storage += 1
self.energy += 50
if random.random() < 0.5:
excretion_voxel = Voxel(position=(x, y, z), voxel_type=EXCRETION)
self.environment.grid[x][y][z] = excretion_voxel
self.state = 'CARING'
elif self.carrying_material and voxel and (voxel.type == NEST or voxel.type == NEST_MATERIAL):
self.build_nest()
self.state = 'FORAGING'
else:
self.follow_pheromone(HOME_PHEROMONE)
def decide_caring(self):
if random.random() < 0.5:
self.care_larvae()
else:
self.state = 'FORAGING'
def care_larvae(self):
x, y, z = int(self.position.x), int(self.position.y), int(self.position.z)
for dx in [-1, 0, 1]:
nx = x + dx
if 0 <= nx < ENV_SIZE[0]:
for dy in [-1, 0, 1]:
ny = y + dy
if 0 <= ny < ENV_SIZE[1]:
nz = z
voxel = self.environment.get_voxel(nx, ny, nz)
if voxel and (voxel.type == LARVA or voxel.type == PUPA):
self.deposit_pheromones() # Hinterlässt Pheromon
self.state = 'FORAGING'
return
self.direction = self.random_direction()
def follow_pheromone(self, pheromone_type):
x, y, z = int(self.position.x), int(self.position.y), int(self.position.z)
max_pheromone = -1
best_direction = None
for dx in [-1, 0, 1]:
nx = x + dx
if 0 <= nx < ENV_SIZE[0]:
for dy in [-1, 0, 1]:
ny = y + dy
if 0 <= ny < ENV_SIZE[1]:
nz = z
neighbor = self.environment.get_voxel(nx, ny, nz)
if neighbor and neighbor.type == AIR:
pheromone_level = neighbor.pheromones[pheromone_type]
if pheromone_level > max_pheromone:
max_pheromone = pheromone_level
best_direction = Vec3(dx, dy, 0)
if best_direction:
self.direction = best_direction.normalized() + Vec3(random.uniform(-0.1, 0.1),
random.uniform(-0.1, 0.1),
0)
else:
self.direction = self.random_direction()
def move(self):
if self.direction.length() == 0:
return # Keine Bewegung
self.direction = self.direction.normalized()
new_position = self.position + self.direction * ANT_SPEED
x, y, z = int(new_position.x), int(new_position.y), int(new_position.z)
if 0 <= x < ENV_SIZE[0] and 0 <= y < ENV_SIZE[1] and 0 <= z < ENV_SIZE[2]:
voxel = self.environment.get_voxel(x, y, z)
if voxel and voxel.type == AIR:
self.position = new_position
self.energy -= 0.1
else:
self.direction = self.random_direction()
else:
self.direction = self.random_direction()
if self.carrying_material:
self.carrying_material.position = self.position + Vec3(0, 0, 0.2)
def act(self):
if self.state == 'FORAGING':
self.search_for_resources()
elif self.state == 'DEPOSITING':
self.decide_depositing()
elif self.state == 'CARING':
self.decide_caring()
def search_for_resources(self):
x, y, z = int(self.position.x), int(self.position.y), int(self.position.z)
voxel = self.environment.get_voxel(x, y, z)
if not self.has_food and not self.carrying_material:
if voxel and voxel.type == FOOD:
self.has_food = True
self.environment.grid[x][y][z] = Voxel(position=(x, y, z), voxel_type=AIR)
self.state = 'DEPOSITING'
elif voxel and voxel.type == SOIL and random.random() < 0.1:
self.carrying_material = Voxel(position=self.position + Vec3(0, 0, 0.2), voxel_type=DEBRIS, carried=True)
self.carrying_material.parent = self
self.environment.grid[x][y][z] = Voxel(position=(x, y, z), voxel_type=AIR)
self.state = 'DEPOSITING'
def deposit_pheromones(self):
x, y, z = int(self.position.x), int(self.position.y), int(self.position.z)
voxel = self.environment.get_voxel(x, y, z)
if voxel:
if self.has_food:
voxel.pheromones[HOME_PHEROMONE] += PHEROMONE_DEPOSIT_AMOUNT
else:
voxel.pheromones[FOOD_PHEROMONE] += PHEROMONE_DEPOSIT_AMOUNT
def build_nest(self):
x, y, z = int(self.position.x), int(self.position.y), int(self.position.z)
best_nx, best_ny, best_nz = -1, -1, -1
max_neighbors = -1
for dx in [-1, 0, 1]:
nx = x + dx
if 0 <= nx < ENV_SIZE[0]:
for dy in [-1, 0, 1]:
ny = y + dy
if 0 <= ny < ENV_SIZE[1]:
for dz in [-1, 0, 1]:
nz = z + dz
if 0 <= nz < ENV_SIZE[2]:
neighbor = self.environment.get_voxel(nx, ny, nz)
if neighbor and neighbor.type == AIR:
neighbors = self.count_neighbors(nx, ny, nz, NEST_MATERIAL)
if neighbors > max_neighbors:
max_neighbors = neighbors
best_nx, best_ny, best_nz = nx, ny, nz
if best_nx != -1:
self.environment.grid[best_nx][best_ny][best_nz] = Voxel(position=(best_nx, best_ny, best_nz), voxel_type=NEST_MATERIAL)
destroy(self.carrying_material)
self.carrying_material = None
def count_neighbors(self, x, y, z, voxel_type):
count = 0
for dx in [-1, 0, 1]:
nx = x + dx
if 0 <= nx < ENV_SIZE[0]:
for dy in [-1, 0, 1]:
ny = y + dy
if 0 <= ny < ENV_SIZE[1]:
for dz in [-1, 0, 1]:
nz = z + dz
if 0 <= nz < ENV_SIZE[2]:
neighbor = self.environment.get_voxel(nx, ny, nz)
if neighbor and neighbor.type == voxel_type:
count += 1
return count
def update(self):
self.age += 1
if self.age > self.max_age or self.energy <= 0:
self.die()
return
self.decide()
self.move()
self.act()
self.deposit_pheromones()
for leg in self.legs:
leg.update_leg()
def die(self):
if self.carrying_material:
self.carrying_material.visible = True
self.carrying_material.carried = False
self.carrying_material.parent = scene
self.environment.grid[int(self.position.x)][int(self.position.y)][int(self.position.z)] = self.carrying_material
self.carrying_material = None
self.colony.remove_ant(self)
destroy(self)
class QueenAnt(Entity):
def __init__(self, position, colony):
super().__init__(
parent=scene,
position=position,
model='sphere',
color=ANT_COLORS['QUEEN'],
scale=0.5
)
self.colony = colony
self.laying_timer = 500
def update(self):
self.laying_timer -= 1
if self.laying_timer <= 0:
if self.colony.food_storage >= 5:
self.colony.food_storage -= 5
self.lay_egg()
self.laying_timer = 500
def lay_egg(self):
larva = Larva(position=self.position, colony=self.colony)
self.colony.add_larva(larva)
class Larva(Entity):
def __init__(self, position, colony):
super().__init__(
parent=scene,
position=position,
model='sphere',
color=ANT_COLORS['LARVA'],
scale=0.1
)
self.colony = colony
self.growth_time = 1000
def update(self):
self.growth_time -= 1
if self.growth_time <= 0:
self.pupate()
def pupate(self):
pupa = Pupa(position=self.position, colony=self.colony)
self.colony.add_pupa(pupa)
destroy(self)
self.colony.larvae.remove(self)
class Pupa(Entity):
def __init__(self, position, colony):
super().__init__(
parent=scene,
position=position,
model='sphere',
color=ANT_COLORS['PUPA'],
scale=0.1
)
self.colony = colony
self.hatching_time = 1000
def update(self):
self.hatching_time -= 1
if self.hatching_time <= 0:
self.hatch()
def hatch(self):
ant = Ant(position=self.position, colony=self.colony, environment=self.colony.environment)
self.colony.add_ant(ant)
destroy(self)
self.colony.pupae.remove(self)
class Colony:
def __init__(self, environment):
self.environment = environment
self.ants = []
self.larvae = []
self.pupae = []
self.food_storage = 0
nest_x, nest_y = ENV_SIZE[0] // 2, ENV_SIZE[1] // 2
nest_z = environment.get_surface_z(nest_x, nest_y)
self.nest_position = Vec3(nest_x, nest_y, nest_z)
self.queen = QueenAnt(position=self.nest_position, colony=self)
def add_ant(self, ant):
self.ants.append(ant)
def remove_ant(self, ant):
if ant in self.ants:
self.ants.remove(ant)
def add_larva(self, larva):
self.larvae.append(larva)
def add_pupa(self, pupa):
self.pupae.append(pupa)
def update(self):
self.queen.update()
for larva in self.larvae[:]:
larva.update()
for pupa in self.pupae[:]:
pupa.update()
class CameraController(Entity):
def __init__(self):
super().__init__()
self.camera_pivot = Entity()
camera.parent = self.camera_pivot
camera.position = (0, -30, 20)
camera.rotation_x = 30
self.rotation_speed = 100
self.zoom_speed = 20
self.current_zoom = 30
self.focus_position = Vec3(ENV_SIZE[0] // 2, ENV_SIZE[1] // 2, ENV_SIZE[2] // 2)
self.camera_pivot.position = self.focus_position
self.following = False
self.follow_target = None
def update(self):
dt = time.dt
if mouse.left and mouse.left_double_click:
hit_entity = mouse.hovered_entity
if isinstance(hit_entity, Voxel) or isinstance(hit_entity, Ant):
self.focus_position = hit_entity.position
self.follow_target = hit_entity if isinstance(hit_entity, Ant) else None
self.following = isinstance(hit_entity, Ant)
if held_keys['right mouse']:
delta = Vec2(mouse.velocity[0], mouse.velocity[1])
self.camera_pivot.rotation_y += delta.x * dt * self.rotation_speed
self.camera_pivot.rotation_x -= delta.y * dt * self.rotation_speed
self.camera_pivot.rotation_x = max(-90, min(90, self.camera_pivot.rotation_x))
self.current_zoom -= mouse.scroll_y * self.zoom_speed * dt
self.current_zoom = max(5, min(100, self.current_zoom))
camera.position = Vec3(0, -self.current_zoom, self.current_zoom * 0.5)
if self.following and self.follow_target:
self.focus_position = self.follow_target.position
self.camera_pivot.position = lerp(self.camera_pivot.position, self.focus_position, 0.1)
# -----------------------
# Hilfsfunktionen
# -----------------------
def update_time_of_day():
global TIME_OF_DAY, IS_DAY
TIME_OF_DAY += TIME_SPEED
if TIME_OF_DAY >= 24:
TIME_OF_DAY = 0
IS_DAY = 6 <= TIME_OF_DAY <= 18 # Tag von 6 bis 18 Uhr
def load_environment_and_colony():
environment = Environment()
colony = Colony(environment)
print("Kolonie wird initialisiert...")
# Anfangs-Ameisen erstellen
for _ in range(INITIAL_WORKERS):
offset = Vec3(random.uniform(-5, 5), random.uniform(-5, 5), 0)
position = colony.nest_position + offset
position.x = max(0, min(position.x, ENV_SIZE[0] - 1))
position.y = max(0, min(position.y, ENV_SIZE[1] - 1))
position.z = environment.get_surface_z(int(position.x), int(position.y))
ant = Ant(position=position, colony=colony, environment=environment)
colony.add_ant(ant)
# Soldatenameisen erstellen
for _ in range(INITIAL_SOLDIERS):
offset = Vec3(random.uniform(-5, 5), random.uniform(-5, 5), 0)
position = colony.nest_position + offset
position.x = max(0, min(position.x, ENV_SIZE[0] - 1))
position.y = max(0, min(position.y, ENV_SIZE[1] - 1))
position.z = environment.get_surface_z(int(position.x), int(position.y))
ant = Ant(position=position, colony=colony, environment=environment, caste='SOLDIER')
colony.add_ant(ant)
print("Kolonie initialisiert.")
return environment, colony
def main():
app = Ursina()
environment, colony = load_environment_and_colony()
# UI-Text für gesammeltes Essen und Tageszeit
food_text = Text(text='', position=window.top_left, origin=(0, 0), background=True)
time_text = Text(text='', position=window.top_right, origin=(1, 0), background=True)
camera_controller = CameraController()
def update():
global current_time_step
current_time_step += 1
environment.update_pheromones()
environment.spawn_food()
environment.apply_physics()
update_time_of_day()
# Ameisen aktualisieren
for ant in colony.ants[:]:
ant.update()
# Kolonie aktualisieren
colony.update()
# UI aktualisieren
food_text.text = f"Essen gesammelt: {colony.food_storage}"
time_text.text = f"Zeit: {TIME_OF_DAY:.2f} Uhr ({'Tag' if IS_DAY else 'Nacht'})"
app.run()
if __name__ == "__main__":
main()