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
Dreamy aulikhan.kakemovАулихан Какемов
Uploaded Jun 21, 2026