-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtube.py
More file actions
132 lines (100 loc) · 4.62 KB
/
tube.py
File metadata and controls
132 lines (100 loc) · 4.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import pygame
import math
# --- 設定定数 ---
WIDTH, HEIGHT = 800, 600
FPS = 60
# 色の定義
BLACK = (0, 0, 0)
# 3Dチューブの設定
NUM_RINGS = 30 # リングの数を増やして奥行きを出す
SEGMENTS = 12 # 円の滑らかさ
RING_SPACING = 40 # リング間の距離
TUBE_RADIUS = 150 # チューブの太さ
SPEED = 8.0 # 進む速度
ROTATION_SPEED = 0.03 # 回転速度
# 視点の設定
FOV = 400 # 視野角
# ★うねうね(カーブ)の設定パラメータ
CURVE_AMP_X = 200 # 横揺れの大きさ(振幅)
CURVE_AMP_Y = 100 # 縦揺れの大きさ
CURVE_FREQ_T = 0.001 # 時間による変化の速さ(うねりの速さ)
CURVE_FREQ_Z = 0.005 # 奥行きによる変化の細かさ(道の曲がり具合)
def main():
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Winding Tube Effect")
clock = pygame.time.Clock()
# リングの初期化
rings = []
for i in range(NUM_RINGS):
z = (NUM_RINGS - i) * RING_SPACING
rings.append({'z': z})
running = True
global_rotation = 0
while running:
# --- イベント処理 ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
# --- 更新処理 ---
time_ms = pygame.time.get_ticks() # 現在時刻(ミリ秒)
global_rotation += ROTATION_SPEED
# リングの移動処理
for ring in rings:
ring['z'] -= SPEED
# 視点の後ろに行ったら一番奥へリセット
if ring['z'] <= 10:
ring['z'] += NUM_RINGS * RING_SPACING
# 描画順序を整えるためZでソート(奥から描く)
rings.sort(key=lambda r: -r['z'])
# --- 描画処理 ---
screen.fill(BLACK)
base_center_x = WIDTH // 2
base_center_y = HEIGHT // 2
prev_points = [] # 前のリングの頂点座標保持用
for idx, ring in enumerate(rings):
current_points = []
scale = FOV / (ring['z'] + 0.1)
# ★ここがポイント:うねうね計算★
# 時間(time_ms) と 奥行き(z) の両方を使ってズレを作る
# sin(時間 + 奥行き * 係数) * 振幅
offset_x = math.sin(time_ms * CURVE_FREQ_T + ring['z'] * CURVE_FREQ_Z) * CURVE_AMP_X
offset_y = math.cos(time_ms * CURVE_FREQ_T * 1.3 + ring['z'] * CURVE_FREQ_Z) * CURVE_AMP_Y
# リングごとの中心座標を決定
ring_center_x = base_center_x + offset_x
ring_center_y = base_center_y + offset_y
# 色の計算(奥ほど暗く、かつ少し色味を変える演出)
depth_ratio = 1 - (ring['z'] / (NUM_RINGS * RING_SPACING))
depth_ratio = max(0, min(1, depth_ratio))
# 色を少しサイバーパンク風に(紫〜青)
r = int(50 * (depth_ratio ** 2))
g = int(200 * (depth_ratio ** 2))
b = int(255 * (depth_ratio ** 2))
color = (r, g, b)
# 各頂点の計算
for i in range(SEGMENTS):
theta = (math.pi * 2 / SEGMENTS) * i + global_rotation
x3d = math.cos(theta) * TUBE_RADIUS
y3d = math.sin(theta) * TUBE_RADIUS
# 中心座標(ring_center)を基準に投影
x2d = ring_center_x + int(x3d * scale)
y2d = ring_center_y + int(y3d * scale)
current_points.append((x2d, y2d))
# 描画(一番奥の出現時のチラつき防止のため、遠すぎるものは描かない)
if ring['z'] < NUM_RINGS * RING_SPACING - 20:
# リング描画
pygame.draw.lines(screen, color, True, current_points, 2)
# 縦ライン(前のリングとつなぐ)
if idx > 0:
for i in range(SEGMENTS):
# 奥の方の線は細く、手前は太くするとより立体的になる(ここでは簡易的に1)
pygame.draw.line(screen, color, prev_points[i], current_points[i], 1)
prev_points = current_points
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
if __name__ == "__main__":
main()