Python
from ursina import *
import random

# --- НАСТРОЙКИ ИГРЫ ---
class Game:
    def __init__(self):
        self.app = Ursina()
        window.title = "Python 3D Shooter"
        window.borderless = False
        window.fullscreen = False
        window.color = color.black
        
        # Переменные состояния
        self.score = 0
        self.health = 100
        self.current_weapon_idx = 0
        self.bots = []
        self.bullets = []
        
        # Создание мира
        self.create_world()
        self.create_player()
        self.create_weapons()
        self.create_ui()
        
        # Запуск цикла спавна ботов
        invoke(self.spawn_bot_loop, delay=2)

    def create_world(self):
        # Пол
        Entity(model='cube', scale=(50, 1, 50), position=(0, -1, 0), 
               texture='white_cube', color=color.gray, collider='box')
        # Стены (простые кубы)
        for i in range(20):
            x, z = random.randint(-20, 20), random.randint(-20, 20)
            if abs(x) < 5 and abs(z) < 5: continue # Не ставить в центре
            Entity(model='cube', scale=(2, 4, 2), position=(x, 2, z), 
                   color=color.dark_gray, collider='box')

    def create_player(self):
        # Игрок - это камера от первого лица
        self.player = FirstPersonController()
        self.player.position = (0, 2, 0)
        self.player.cursor.visible = True
        self.player.gravity = 0.5
        self.player.speed = 8
        
        # Оружие в руках (визуально)
        self.gun_model = Entity(model='cube', scale=(0.2, 0.2, 1), 
                                parent=self.player.camera_pivot, 
                                position=(0.5, -0.5, 1), 
                                color=color.gray)
        self.muzzle_flash = Entity(model='quad', scale=0.3, parent=self.gun_model, 
                                   position=(0, 0, 0.6), color=color.yellow, enabled=False)

    def create_weapons(self):
        # Характеристики оружия: [Имя, Скорострельность(сек), Урон, Разброс, Цвет]
        self.weapons = [
            {"name": "Пистолет", "rate": 0.4, "damage": 25, "spread": 0.02, "color": color.yellow},
            {"name": "Автомат",  "rate": 0.1, "damage": 12, "spread": 0.05, "color": color.green},
            {"name": "Дробовик", "rate": 1.0, "damage": 15, "spread": 0.15, "color": color.red, "pellets": 6},
            {"name": "Снайперка","rate": 1.5, "damage": 100,"spread": 0.001,"color": color.blue}
        ]
        self.last_shot_time = 0

    def create_ui(self):
        # Текст здоровья
        self.hp_text = Text(text=f'HP: {self.health}', position=(-0.8, 0.45), 
                            scale=2, color=color.green)
        # Текст патронов/оружия
        self.weapon_text = Text(text=f'Weapon: Пистолет', position=(-0.8, 0.4), 
                                scale=1.5, color=color.white)
        # Счет
        self.score_text = Text(text=f'Score: 0', position=(0.5, 0.45), 
                               scale=2, color=color.white)
        # Прицел
        self.crosshair = Entity(model='quad', parent=camera.ui, scale=0.01, 
                                color=color.white, z=-1)
        cross_line_h = Entity(model='quad', parent=camera.ui, scale=(0.02, 0.002), 
                              color=color.white, z=-1)
        cross_line_v = Entity(model='quad', parent=camera.ui, scale=(0.002, 0.02), 
                              color=color.white, z=-1)

    def spawn_bot_loop(self):
        if len(self.bots) < 5 + int(self.score / 100): # Сложнее со временем
            self.spawn_bot()
        invoke(self.spawn_bot_loop, delay=random.uniform(1, 3))

    def spawn_bot(self):
        angle = random.uniform(0, 360)
        dist = random.uniform(15, 30)
        x = self.player.x + math.sin(angle) * dist
        z = self.player.z + math.cos(angle) * dist
        
        bot = Bot(position=(x, 2, z), game_ref=self)
        self.bots.append(bot)

    def shoot(self):
        now = time.time()
        weapon = self.weapons[self.current_weapon_idx]
        
        if now - self.last_shot_time < weapon['rate']:
            return
        
        self.last_shot_time = now
        
        # Визуальный эффект выстрела
        self.muzzle_flash.enabled = True
        invoke(setattr, self.muzzle_flash, 'enabled', False, delay=0.05)
        
        # Отдача камеры
        camera.pitch -= 2 
        
        pellets = weapon.get('pellets', 1)
        
        for _ in range(pellets):
            # Расчет направления с разбросом
            direction = Vec3(0, 0, 1)
            direction.x += random.uniform(-weapon['spread'], weapon['spread'])
            direction.y += random.uniform(-weapon['spread'], weapon['spread'])
            direction = camera.forward + direction.normalized() * 0.1
            
            # Создаем пулю (Raycast для мгновенного попадания hitscan)
            hit_info = raycast(camera.world_position + direction, direction, 
                               distance=100, ignore=[self.player])
            
            # Визуализация трассера
            tracer = Entity(model='line', thickness=2, 
                            start=camera.world_position + direction*2, 
                            end=hit_info.world_point if hit_info.hit else camera.world_position + direction*100,
                            color=weapon['color'])
            destroy(tracer, delay=0.1)
            
            if hit_info.hit:
                if hasattr(hit_info.entity, 'take_damage'):
                    hit_info.entity.take_damage(weapon['damage'])

    def switch_weapon(self, index):
        self.current_weapon_idx = index % len(self.weapons)
        w_name = self.weapons[self.current_weapon_idx]['name']
        self.weapon_text.text = f'Weapon: {w_name}'
        self.gun_model.color = self.weapons[self.current_weapon_idx]['color']

    def update(self):
        # Управление оружием
        if mouse.left:
            self.shoot()
            
        if held_keys['1']: self.switch_weapon(0)
        if held_keys['2']: self.switch_weapon(1)
        if held_keys['3']: self.switch_weapon(2)
        if held_keys['4']: self.switch_weapon(3)
        
        # Обновление UI
        self.hp_text.text = f'HP: {int(self.health)}'
        self.score_text.text = f'Score: {self.score}'
        
        if self.health <= 0:
            application.quit() # Или экран смерти

    def take_damage(self, amount):
        self.health -= amount
        self.hp_text.color = color.red
        invoke(setattr, self.hp_text, 'color', color.green, delay=0.2)

# --- КЛАСС БОТА ---
class Bot(Entity):
    def __init__(self, position, game_ref):
        super().__init__(
            model='cube', 
            color=color.red, 
            position=position, 
            scale=(1.5, 3, 1.5), 
            collider='box'
        )
        self.game = game_ref
        self.health = 50
        self.speed = 3
        self.last_attack = 0
        
        # Глаза (чтобы видеть куда смотрит)
        self.eyes = Entity(parent=self, model='cube', scale=(0.5, 0.5, 0.5), 
                           position=(0, 1, 0.8), color=color.black)

    def update(self):
        if not self.game.player: return
        
        dist = self.distance_to(self.game.player)
        
        # Поворот к игроку
        self.look_at(self.game.player.position)
        
        # Движение
        if dist > 3:
            self.position += self.forward * self.speed * time.dt
        else:
            # Атака ближнего боя
            if time.time() - self.last_attack > 1:
                self.game.take_damage(10)
                self.last_attack = time.time()
                # Эффект удара
                self.color = color.white
                invoke(setattr, self, 'color', color.red, delay=0.1)

    def take_damage(self, amount):
        self.health -= amount
        # Эффект попадания
        self.color = color.white
        invoke(setattr, self, 'color', color.red, delay=0.1)
        
        if self.health <= 0:
            self.die()

    def die(self):
        self.game.score += 10
        if self in self.game.bots:
            self.game.bots.remove(self)
        
        # Частицы смерти
        for i in range(5):
            p = Entity(model='cube', scale=0.3, position=self.position, 
                       color=color.orange, velocity=Vec3(random.uniform(-5,5), random.uniform(0,5), random.uniform(-5,5)))
            p.animate_y(p.y - 2, duration=0.5)
            destroy(p, delay=0.5)
            
        destroy(self)

# --- ЗАПУСК ---
if __name__ == '__main__':
    game = Game()
    game.app.run()

шутир

Sign in to react