Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__pycache__/
*.pyc
30 changes: 26 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,38 @@
# Python Visual Effects 🎬✨
# Python Visual Effects & Games 🎬✨🎮

Python で作成した美しい視覚効果とアニメーションのコレクション
Python で作成した美しい視覚効果とアニメーション、そして全力で作ったシューティングゲームのコレクション

## 機能 🌟

### 🎮 Ultimate Python Shooter (シューティングゲーム)
ターミナルで遊べる本格的なシューティングゲームです。

- **ステージシステム**: 敵を倒してステージを進めよう!
- **ボスバトル**: 強力なボスが登場!
- **パワーアップ**: アイテムを取ってショットを強化!
- **ボム**: 画面内の敵を一掃する必殺技!

### 🎨 Visual Effects
- **🌈 レインボーテキスト**: 文字が色とりどりに表示される効果
- **📝 タイプライター効果**: 文字が一文字ずつゆっくり現れる演出
- **🖍️ カラータイプライター効果**: 文字ごとに色が変わるタイプライティング
- **🔄 スピナーエフェクト**: くるくる回るスピナーアニメーション
- **💻 マトリックス風エフェクト**: 日本語文字が緑色で画面を流れる
- **💥 爆発アニメーション**: 殡階的に拡大する爆発エフェクト
- **💥 爆発アニメーション**: 段階的に拡大する爆発エフェクト

## 実行方法 🚀

### シューティングゲーム
```bash
python3 shooting_game.py
```
**操作方法**:
- **矢印キー**: 移動
- **スペース**: ショット / 決定
- **B**: ボム発射
- **Q**: 終了 / 中断

### ビジュアルエフェクト
```bash
python3 hello.py
```
Expand All @@ -21,10 +41,11 @@ python3 hello.py

- Python 3.x
- ターミナル/コマンドプロンプト (カラー表示対応)
- Linux環境推奨(`curses` ライブラリを使用するため)

## デモ 🎥

実行すると以下のエフェクトが順番に表示されます:
実行すると以下のエフェクトが順番に表示されます(`hello.py`)

1. 長文のタイプライター効果でメッセージが表示
2. カラータイプライター効果
Expand All @@ -38,6 +59,7 @@ python3 hello.py
- **カラーエフェクト**: ANSI エスケープコードを使用
- **アニメーション**: time.sleep() による時間制御
- **文字セット**: 日本語ひらがな + 数字の組み合わせ
- **ゲームライブラリ**: `curses` (標準ライブラリ)

## 作成者 👨‍💻

Expand Down
331 changes: 331 additions & 0 deletions shooting_game.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
import curses
import random
import time

class Bullet:
def __init__(self, x, y, dy, dx=0, char='|'):
self.x = x
self.y = y
self.dy = dy
self.dx = dx
self.char = char

def update(self):
self.y += self.dy
self.x += self.dx

def draw(self, stdscr):
try:
if self.y >= 0:
stdscr.addch(int(self.y), int(self.x), self.char)
except curses.error:
pass

class Enemy:
def __init__(self, x, y, dy, hp=1, char='V'):
self.x = x
self.y = y
self.dy = dy
self.hp = hp
self.char = char

def update(self, max_x):
self.y += self.dy

def draw(self, stdscr):
try:
if self.y >= 0:
stdscr.addch(int(self.y), int(self.x), self.char)
except curses.error:
pass

class Boss(Enemy):
def __init__(self, x, y):
super().__init__(x, y, 0.1, hp=50, char='W')
self.move_dir = 1
self.move_timer = 0
self.shoot_timer = 0

def update(self, max_x):
self.y += self.dy

if self.y > 5:
self.dy = 0

self.move_timer += 1
if self.move_timer > 5:
self.x += self.move_dir
self.move_timer = 0
if self.x <= 2 or self.x >= max_x - 3:
self.move_dir *= -1

class PowerUp:
def __init__(self, x, y):
self.x = x
self.y = y
self.dy = 0.5
self.char = 'P'

def update(self):
self.y += self.dy

def draw(self, stdscr):
try:
stdscr.addch(int(self.y), int(self.x), self.char)
except curses.error:
pass

class Explosion:
def __init__(self, x, y):
self.x = x
self.y = y
self.life = 5
self.chars = ['.', 'o', 'O', '@', '*']

def update(self):
self.life -= 1

def draw(self, stdscr):
if self.life > 0:
char = self.chars[min(len(self.chars)-1, self.life)]
try:
stdscr.addch(int(self.y), int(self.x), char)
except curses.error:
pass

class Player:
def __init__(self, x, y, char='^'):
self.x = x
self.y = y
self.char = char
self.power = 1
self.bombs = 2

def move(self, dx, dy, max_x, max_y):
self.x = max(0, min(max_x - 1, self.x + dx))
self.y = max(0, min(max_y - 1, self.y + dy))

def shoot(self):
bullets = []
bullets.append(Bullet(self.x, self.y - 1, -1, 0, '|'))
if self.power >= 2:
bullets.append(Bullet(self.x - 1, self.y - 1, -1, -0.3, '/'))
bullets.append(Bullet(self.x + 1, self.y - 1, -1, 0.3, '\\'))
return bullets

def draw(self, stdscr):
try:
stdscr.addch(int(self.y), int(self.x), self.char)
except curses.error:
pass

def check_collision(obj1, obj2):
return int(obj1.x) == int(obj2.x) and int(obj1.y) == int(obj2.y)

def draw_title(stdscr, sh, sw):
stdscr.clear()
title = "ULTIMATE PYTHON SHOOTER"
try:
stdscr.addstr(sh // 2 - 2, sw // 2 - len(title) // 2, title, curses.A_BOLD)
msg = "Press SPACE to Start"
stdscr.addstr(sh // 2, sw // 2 - len(msg) // 2, msg)
except curses.error:
pass
stdscr.refresh()
while True:
key = stdscr.getch()
if key == ord(' '):
return True
elif key == ord('q'):
return False
time.sleep(0.05)

def draw_gameover(stdscr, sh, sw, score):
stdscr.clear()
msg = "GAME OVER"
try:
stdscr.addstr(sh // 2 - 2, sw // 2 - len(msg) // 2, msg, curses.A_BOLD)
score_msg = f"Final Score: {score}"
stdscr.addstr(sh // 2, sw // 2 - len(score_msg) // 2, score_msg)
retry_msg = "Press SPACE to Restart or 'q' to Quit"
stdscr.addstr(sh // 2 + 2, sw // 2 - len(retry_msg) // 2, retry_msg)
except curses.error:
pass
stdscr.refresh()
while True:
key = stdscr.getch()
if key == ord(' '):
return True
elif key == ord('q'):
return False
time.sleep(0.05)

def game_loop(stdscr, sh, sw):
player = Player(sw // 2, sh - 2)
bullets = []
enemies = []
powerups = []
explosions = []

score = 0
lives = 3
level = 1
enemies_spawned = 0
boss_spawned = False

enemy_spawn_timer = 0

running = True
while running:
stdscr.clear()

# Spawn enemies
if not boss_spawned:
enemy_spawn_timer += 1
if enemy_spawn_timer > max(5, 25 - level * 2):
enemies.append(Enemy(random.randint(1, sw - 2), 0, 0.2 + random.random() * 0.1 * level, hp=1 + level//2))
enemies_spawned += 1
enemy_spawn_timer = 0

if enemies_spawned > 10 * level and len(enemies) == 0:
boss = Boss(sw // 2, -3)
enemies.append(boss)
boss_spawned = True

# Update entities
player.draw(stdscr)

for bullet in bullets[:]:
bullet.update()
bullet.draw(stdscr)
if bullet.y < 0 or bullet.y >= sh or bullet.x < 0 or bullet.x >= sw:
bullets.remove(bullet)

for enemy in enemies[:]:
enemy.update(sw)
enemy.draw(stdscr)
if enemy.y >= sh:
enemies.remove(enemy)

# Collision with Player
if check_collision(player, enemy):
lives -= 1
explosions.append(Explosion(enemy.x, enemy.y))
enemies.remove(enemy)
if isinstance(enemy, Boss):
boss_spawned = False
enemies_spawned = 0
level += 1
score += 500

if lives <= 0:
running = False

# Collision: Bullets vs Enemies
for bullet in bullets[:]:
hit = False
for enemy in enemies[:]:
if check_collision(bullet, enemy):
enemy.hp -= 1
hit = True
explosions.append(Explosion(bullet.x, bullet.y))
if enemy.hp <= 0:
if enemy in enemies: enemies.remove(enemy)
score += 10 * level
explosions.append(Explosion(enemy.x, enemy.y))
if isinstance(enemy, Boss):
score += 1000
level += 1
enemies_spawned = 0
boss_spawned = False
lives += 1
elif random.random() < 0.1:
powerups.append(PowerUp(enemy.x, enemy.y))
break
if hit:
bullets.remove(bullet)

# Update and Draw PowerUps
for pu in powerups[:]:
pu.update()
pu.draw(stdscr)
if pu.y >= sh:
powerups.remove(pu)
elif check_collision(player, pu):
player.power = min(3, player.power + 1)
powerups.remove(pu)
score += 50
player.bombs = min(5, player.bombs + 1)

# Update and Draw Explosions
for exp in explosions[:]:
exp.update()
exp.draw(stdscr)
if exp.life <= 0:
explosions.remove(exp)

# UI
try:
stdscr.addstr(0, 0, f"Score: {score} | Lives: {lives} | Level: {level} | Bombs: {player.bombs}")
if boss_spawned:
stdscr.addstr(0, sw - 10, "BOSS!!", curses.A_BLINK)
stdscr.addstr(1, 0, "Move: Arrows | Shoot: Space | Bomb: 'b' | Quit: 'q'")
except curses.error:
pass

stdscr.refresh()

# Input Handling
key = stdscr.getch()

if key == ord('q'):
return score # Quit game but show score? Or just exit? Let's show score.
# running = False
elif key == curses.KEY_LEFT:
player.move(-1, 0, sw, sh)
elif key == curses.KEY_RIGHT:
player.move(1, 0, sw, sh)
elif key == curses.KEY_UP:
player.move(0, -1, sw, sh)
elif key == curses.KEY_DOWN:
player.move(0, 1, sw, sh)
elif key == ord(' '):
bullets.extend(player.shoot())
elif key == ord('b'):
if player.bombs > 0:
player.bombs -= 1
for e in enemies[:]:
e.hp -= 10
explosions.append(Explosion(e.x, e.y))
if e.hp <= 0:
enemies.remove(e)
score += 10
if isinstance(e, Boss):
score += 1000
level += 1
enemies_spawned = 0
boss_spawned = False
lives += 1

time.sleep(0.02)

return score

def main(stdscr):
# Initial Setup
curses.curs_set(0)
stdscr.nodelay(1)
stdscr.timeout(50)

sh, sw = stdscr.getmaxyx()

if not draw_title(stdscr, sh, sw):
return

while True:
score = game_loop(stdscr, sh, sw)
if not draw_gameover(stdscr, sh, sw, score):
break

if __name__ == "__main__":
curses.wrapper(main)
Loading