text-indent无效的几个原因及解决方案

本文详细解析了CSS中text-indent属性的使用方法及其常见问题。包括如何解决text-indent在不同浏览器下的兼容性问题,以及当该属性失效时可能的原因和对应的解决方案。
部署运行你感兴趣的模型镜像
text-indent是用来字符缩进的。我们通常用在段落首字缩进、想用图片显示却要考虑SEO对文字的理解等。虽然好用,但有时它并不是这么的听话,会出现无效的情况。下面整理了一些text-inden失效的原因。

1.text-indent所在的元素是行内元素而非块级元素。比如用在span,a等行内元素上;

解决方案:
在行内元素加上display:block; [color=red] 或者把目标元素放入<div>等块级元素,然后把text-indent
样式应用到<div>等块级元素。[/color]

2.text-indent所在的元素未使用overflow属性;
解决方案:
在元素加上overflow:hidden;

3.text-indent设置的值超出浏览器默认的最大或最小值,例如:text-indent:999999em在谷歌(Chrome)浏览器下无效;

解决方案:
有前辈说Chrome0.4.154.22版本下对text-indent支持的缩进极限值是:-134217723px;超出这个数在Chrome下则text-indent属性无效,FF下也存在同样的问题,不过极限数值是多少没去研究了,ie下似乎没有极限或极限值范围大一些。所以不能随意按住999999不放啊。

来源:颤哥的博客
地址:http://www.kulera.cn/post/text-indent.html

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

import dash from dash import dcc, html from dash.dependencies import Input, Output, State import plotly.graph_objects as go import json import pandas as pd import os import numpy as np import math from shapely.geometry import Point, Polygon from shapely.ops import unary_union # ---------------------------------------------------------------------- # 1. 配置参数读取 # ---------------------------------------------------------------------- CONFIG_FILE = "simulation_parameter_case.json" try: with open(CONFIG_FILE, 'r') as f: config = json.load(f) isd = config["channel_parameters"]["isd"] display_cell_ids = [bs["cell_id"] for bs in config["bs_parameters"]] except Exception as e: print(f"Error: {e} - Using default values") isd = 200 display_cell_ids = [] # ---------------------------------------------------------------------- # 2. 7c3s小区拓扑类定义(添加精确四边形扇区) # ---------------------------------------------------------------------- class CellTopology: def __init__(self, isd=200): self.isd = isd self.radius = (self.isd / 2.0) / (np.cos(np.deg2rad(30))) # 基站位置 self.bs_loc_set = np.array([ [0.0, 0.0], # 中心基站 [-math.sqrt(3) * self.isd / 2.0, self.isd / 2.0], # 左上 [0.0, self.isd], # 上 [math.sqrt(3) * self.isd / 2.0, self.isd / 2.0], # 右上 [math.sqrt(3) * self.isd / 2.0, -self.isd / 2.0], # 右下 [0.0, -self.isd], # 下 [-math.sqrt(3) * self.isd / 2.0, -self.isd / 2.0] # 左下 ]) # 六边形顶点 self.center_bs_hexgon_vertex = np.array([ [self.radius, 0.0], [self.radius / 2.0, self.isd / 2.0], [-self.radius / 2.0, self.isd / 2.0], [-self.radius, 0.0], [-self.radius / 2.0, -self.isd / 2.0], [self.radius / 2.0, -self.isd / 2.0], [self.radius, 0.0] ]) # 扇区分割线 self.center_sector_split_line = np.array([ [0.0, 0.0], [self.radius, 0.0], # 0° [0.0, 0.0], [-self.radius / 2.0, self.isd / 2.0], # 120° [0.0, 0.0], [-self.radius / 2.0, -self.isd / 2.0] # 240° ]) # 存储每个扇区的四边形多边形 self.sector_polygons = {} current_cell_id = 0 # 扇区角度定义 sector_angles = [0, 120, 240] for bs_id in range(7): bs_loc = self.bs_loc_set[bs_id] hex_vertices = self.center_bs_hexgon_vertex + bs_loc # 为每个扇区创建四边形多边形 for i, angle in enumerate(sector_angles): # 扇区起始点和结束点 start_idx = i end_idx = (i + 1) % 3 # 扇区四边形顶点:基站中心 + 三个六边形顶点 quad_points = np.array([ bs_loc, # 基站中心 hex_vertices[start_idx * 2], hex_vertices[start_idx * 2 + 1], hex_vertices[end_idx * 2] ]) self.sector_polygons[current_cell_id] = quad_points current_cell_id += 1 # 使用配置文件中的ISD值初始化拓扑 topology = CellTopology(isd=isd) # ---------------------------------------------------------------------- # 3. UE轨迹工具初始设置 # ---------------------------------------------------------------------- JSON_FILE = "user_specific_parameter.json" # 初始化JSON文件 def initialize_json_file(): if not os.path.exists(JSON_FILE) or os.stat(JSON_FILE).st_size == 0: default_data = { "ue_trajectories": { "trajectory_interval_second": 1, "interpolation_interval_second": 0.01, "trajectory_list": [], "isd": isd, # 添加ISD信息 "display_cell_ids": display_cell_ids # 添加显示的cell_id信息 } } with open(JSON_FILE, 'w') as f: json.dump(default_data, f, indent=4) else: try: with open(JSON_FILE, 'r') as f: data = json.load(f) if "ue_trajectories" in data: # 更新当前ISD和显示的cell_id data["ue_trajectories"]["isd"] = isd data["ue_trajectories"]["display_cell_ids"] = display_cell_ids data["ue_trajectories"]["trajectory_list"] = [] else: data["ue_trajectories"] = { "trajectory_interval_second": 1, "interpolation_interval_second": 0.01, "trajectory_list": [], "isd": isd, "display_cell_ids": display_cell_ids } with open(JSON_FILE, 'w') as f: json.dump(data, f, indent=4) except Exception as e: print(f"初始化JSON文件错误: {e}") default_data = { "ue_trajectories": { "trajectory_interval_second": 1, "interpolation_interval_second": 0.01, "trajectory_list": [], "isd": isd, "display_cell_ids": display_cell_ids } } with open(JSON_FILE, 'w') as f: json.dump(default_data, f, indent=4) # 初始化JSON文件 initialize_json_file() # ---------------------------------------------------------------------- # 4. 背景点生成函数(只生成指定扇区内的点) # ---------------------------------------------------------------------- def generate_background_points(display_cell_ids, density=5): """只为显示的扇区生成背景点""" display_polygons = [] # 获取所有要显示扇区的多边形 for cell_id in display_cell_ids: if cell_id in topology.sector_polygons: quad_points = topology.sector_polygons[cell_id] display_polygons.append(Polygon(quad_points)) if not display_polygons: return [], [] # 合并所有多边形 combined_poly = unary_union(display_polygons) # 计算边界 min_x, min_y, max_x, max_y = combined_poly.bounds min_x -= 10 max_x += 10 min_y -= 10 max_y += 10 # 生成网格点 x_coords = np.linspace(min_x, max_x, int((max_x - min_x) * density)) y_coords = np.linspace(min_y, max_y, int((max_y - min_y) * density)) points_x = [] points_y = [] # 筛选在合并多边形内的点 for x in x_coords: for y in y_coords: p = Point(x, y) if combined_poly.contains(p): points_x.append(x) points_y.append(y) return points_x, points_y # 生成指定扇区的背景点 BG_POINTS_X, BG_POINTS_Y = generate_background_points(display_cell_ids, density=2) # ---------------------------------------------------------------------- # 5. 小区拓扑绘图函数(只绘制指定四边形扇区) # ---------------------------------------------------------------------- def create_initial_figure(all_trajectories_data=[]): fig = go.Figure() # 1. 只绘制指定的四边形扇区 for cell_id in display_cell_ids: if cell_id in topology.sector_polygons: quad_points = topology.sector_polygons[cell_id] # 闭合多边形(第一个点添加到末尾) x_quad = list(quad_points[:, 0]) + [quad_points[0, 0]] y_quad = list(quad_points[:, 1]) + [quad_points[0, 1]] # 添加四边形扇区边界 fig.add_trace(go.Scatter( x=x_quad, y=y_quad, mode='lines', line=dict(color='green', width=2), name=f'Cell {cell_id}', hoverinfo='skip', showlegend=False )) # 2. 添加扇区ID标签 labels_x = [] labels_y = [] labels_text = [] for cell_id in display_cell_ids: if cell_id in topology.sector_polygons: quad_points = topology.sector_polygons[cell_id] # 标签位置:四边形中心点 center_x = np.mean(quad_points[:, 0]) center_y = np.mean(quad_points[:, 1]) labels_x.append(center_x) labels_y.append(center_y) labels_text.append(f"Cell {cell_id}") fig.add_trace(go.Scatter( x=labels_x, y=labels_y, mode='text', text=labels_text, textfont=dict(size=12, color='black'), textposition='middle center', hoverinfo='skip', showlegend=False )) # 3. 添加透明背景点 fig.add_trace(go.Scatter( x=BG_POINTS_X, y=BG_POINTS_Y, mode='markers', marker=dict(size=5, color='rgba(0,0,0,0)', opacity=0), name='Click Area', hoverinfo='none', customdata=[[x, y] for x, y in zip(BG_POINTS_X, BG_POINTS_Y)], unselected=dict(marker={'opacity': 0}), selected=dict(marker={'color': 'rgba(255, 0, 0, 0.5)', 'opacity': 0.5, 'size': 8}), showlegend=False )) # 4. 设置图表布局 axis_range = isd * 1.5 fig.update_layout( title=f'Selected Cells: {display_cell_ids} - UE #{len(all_trajectories_data) + 1}', xaxis_title='X Coordinate (m)', yaxis_title='Y Coordinate (m)', xaxis=dict(range=[-axis_range, axis_range]), yaxis=dict(range=[-axis_range, axis_range], scaleanchor='x', scaleratio=1), dragmode='select', template='plotly_white', clickmode='event+select', height=800 ) return fig # ---------------------------------------------------------------------- # 6. Dash应用布局 # ---------------------------------------------------------------------- app = dash.Dash(__name__) app.layout = html.Div([ html.H1(f"Cell Visualization Tool (ISD={isd}m)"), html.Div([ html.P(f"Displayed Cells: {display_cell_ids}", style={'font-weight': 'bold', 'margin-top': '10px'}) ]), dcc.Graph( id='topology-graph', figure=create_initial_figure(), config={'displayModeBar': True}, style={'height': '80vh', 'width': '100%'} ), html.Div(id='selected-data-output'), html.Div([ html.Button('Save Current UE Trajectory', id='save-clear-button', n_clicks=0, style={'margin-right': '10px', 'padding': '10px', 'background-color': '#4CAF50', 'color': 'white'}), html.Button('Export to JSON', id='export-json-button', n_clicks=0, style={'padding': '10px', 'background-color': '#008CBA', 'color': 'white'}), ], style={'margin': '20px 0'}), dcc.Store(id='current-ue-store', data=[]), dcc.Store(id='all-trajectories-store', data=[]), dcc.Store(id='last-point-coords', data={'x': None, 'y': None}) ]) # ---------------------------------------------------------------------- # 7. Dash回调函数 # ---------------------------------------------------------------------- @app.callback( [Output('current-ue-store', 'data', allow_duplicate=True), Output('last-point-coords', 'data', allow_duplicate=True)], [Input('topology-graph', 'selectedData')], [State('current-ue-store', 'data')], prevent_initial_call=True ) def handle_graph_select(selectedData, current_ue_data): if selectedData is None or not selectedData['points']: return dash.no_update, dash.no_update last_point = selectedData['points'][-1] # 确认选择来自"点击区域"轨迹 if last_point.get('curveNumber') != 25: return dash.no_update, dash.no_update x = last_point['x'] y = last_point['y'] z = 1.5 new_point = [round(x, 4), round(y, 4), z] if current_ue_data and new_point == current_ue_data[-1]: return dash.no_update, dash.no_update updated_trajectory = current_ue_data + [new_point] return updated_trajectory, {'x': round(x, 4), 'y': round(y, 4)} @app.callback( [Output('topology-graph', 'figure', allow_duplicate=True), Output('selected-data-output', 'children')], [Input('current-ue-store', 'data'), Input('last-point-coords', 'data')], [State('all-trajectories-store', 'data')], prevent_initial_call=True ) def update_graph_and_display(current_ue_data, last_point_coords, all_trajectories_data): fig = create_initial_figure(all_trajectories_data) if current_ue_data: df = pd.DataFrame(current_ue_data, columns=['x', 'y', 'z']) fig.add_trace(go.Scatter( x=df['x'], y=df['y'], mode='lines+markers', marker=dict(size=8, color='red', symbol='circle'), line=dict(color='red', width=2), name='UE Trajectory', hoverinfo='text', text=[f'({x}, {y})' for x, y in zip(df['x'], df['y'])], showlegend=True )) if last_point_coords['x'] is not None: display_text = f"Last Point: X={last_point_coords['x']}, Y={last_point_coords['y']}, Height=1.5m" else: display_text = "Drag/click on the chart area to draw UE trajectory..." return fig, display_text @app.callback( [Output('current-ue-store', 'data', allow_duplicate=True), Output('all-trajectories-store', 'data', allow_duplicate=True)], [Input('save-clear-button', 'n_clicks')], [State('current-ue-store', 'data'), State('all-trajectories-store', 'data')], prevent_initial_call=True ) def save_and_clear_trajectory(n_clicks, current_ue_data, all_trajectories_data): if n_clicks > 0: if not current_ue_data: return [], all_trajectories_data updated_all_trajectories = all_trajectories_data + [current_ue_data] new_current_ue_data = [] return new_current_ue_data, updated_all_trajectories return dash.no_update, dash.no_update @app.callback( Output('export-json-button', 'children'), [Input('export-json-button', 'n_clicks')], [State('all-trajectories-store', 'data')], prevent_initial_call=True ) def export_to_json(n_clicks, all_trajectories_data): if n_clicks > 0: try: with open(JSON_FILE, 'r') as f: data = json.load(f) data["ue_trajectories"]["trajectory_list"] = all_trajectories_data with open(JSON_FILE, 'w') as f: json.dump(data, f, indent=4) num_trajectories = len(all_trajectories_data) return f"Exported {num_trajectories} trajectories to {JSON_FILE}" except Exception: return "Export failed!" return 'Export All Trajectories to JSON File' # ---------------------------------------------------------------------- # 7. 运行应用 # ---------------------------------------------------------------------- if __name__ == '__main__': print(f"Please visit http://127.0.0.1:8050/") app.run(debug=True) 上面代码点击UE轨迹后,无反应
11-01
import cv2 import numpy as np import random from collections import deque import mediapipe as mp import json import os from datetime import datetime from PIL import Image, ImageDraw, ImageFont import platform # ------------------------------ # 检测系统并选择合适的中文字体路径 # ------------------------------ def get_chinese_font(): system = platform.system() font_path = None if system == "Windows": font_path = "C:/Windows/Fonts/simhei.ttf" # 黑体 elif system == "Darwin": # macOS font_path = "/System/Library/Fonts/PingFang.ttc" elif system == "Linux": for path in [ "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc" ]: if os.path.exists(path): font_path = path break try: return ImageFont.truetype(font_path, 32) if font_path else ImageFont.load_default() except Exception as e: print(f"⚠️ 加载中文字体失败: {e}") return ImageFont.load_default() CHINESE_FONT = get_chinese_font() # ------------------------------ # 在 OpenCV 图像上绘制中文(使用 PIL) # ------------------------------ def put_chinese_text(image, text, position, color=(255, 255, 255), font_size=30): """使用 Pillow 绘制中文文本""" try: if hasattr(CHINESE_FONT, 'path'): font = ImageFont.truetype(CHINESE_FONT.path, font_size) else: font = ImageFont.load_default() print("⚠️ 使用默认字体渲染中文") except: font = ImageFont.load_default() pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) draw = ImageDraw.Draw(pil_image) draw.text(position, text, fill=tuple(color), font=font) return cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR) # ------------------------------ # 排行榜管理 # ------------------------------ RANKING_FILE = "snake_ranking.json" MAX_RECORDS = 100 def load_ranking(): if not os.path.exists(RANKING_FILE): return [] try: with open(RANKING_FILE, 'r', encoding='utf-8') as f: data = json.load(f) seen = set() unique_data = [] for item in data: key = (item.get('score', 0), item.get('timestamp', '')) if key not in seen: seen.add(key) unique_data.append(item) return sorted(unique_data, key=lambda x: x['score'], reverse=True) except Exception as e: print("加载排行榜失败:", e) return [] def save_best_score(score): ranking = load_ranking() current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") new_record = {"score": score, "timestamp": current_time} ranking.append(new_record) seen = set() unique_ranking = [] for r in ranking: key = (r['score'], r['timestamp']) if key not in seen: seen.add(key) unique_ranking.append(r) unique_ranking = sorted(unique_ranking, key=lambda x: x['score'], reverse=True)[:MAX_RECORDS] try: with open(RANKING_FILE, 'w', encoding='utf-8') as f: json.dump(unique_ranking, f, indent=2, ensure_ascii=False) except Exception as e: print("保存排行榜失败:", e) def clear_ranking(): frame = np.zeros((720, 1280, 3), dtype=np.uint8) for y in range(720): color_val = 20 + y // 20 frame[y, :] = [color_val, color_val // 2, 60] h, w = frame.shape[:2] center_x = w // 2 frame = put_chinese_text(frame, "⚠️ 清空所有记录?", (center_x - 240, h // 2 - 60), color=(0, 0, 255), font_size=48) frame = put_chinese_text(frame, "确定要清空吗?(Y=是, N=否)", (center_x - 260, h // 2), color=(255, 255, 255), font_size=36) frame = put_chinese_text(frame, "此操作不可撤销!", (center_x - 180, h // 2 + 50), color=(150, 150, 150), font_size=32) cv2.imshow("Hand-Controlled Snake Game", frame) while True: key = cv2.waitKey(1) & 0xFF if key in (ord('y'), ord('Y')): try: with open(RANKING_FILE, 'w') as f: f.write("[]") return True except Exception as e: print("清空失败:", e) return False elif key in (ord('n'), ord('N'), 27): # ESC return False # ------------------------------ # 主菜单界面 # ------------------------------ def show_ranking_menu(): ranking = load_ranking() frame = np.zeros((720, 1280, 3), dtype=np.uint8) for y in range(720): color_val = 20 + y // 20 frame[y, :] = [color_val, color_val // 2, 60] h, w = frame.shape[:2] center_x = w // 2 frame = put_chinese_text(frame, "🏆 贪吃蛇游戏排行榜 🏆", (center_x - 280, 40), color=(255, 255, 100), font_size=50) frame = put_chinese_text(frame, "名次 得分 时间", (center_x - 240, 90), color=(200, 255, 255), font_size=30) start_y = 140 for i, item in enumerate(ranking[:10]): text = f"{i + 1:2d} {item['score']:4d} {item['timestamp']}" color = (0, 255, 255) if i == 0 else (200, 255, 200) frame = put_chinese_text(frame, text, (center_x - 240, start_y + i * 32), color=color, font_size=28) if len(ranking) == 0: frame = put_chinese_text(frame, "暂无记录...", (center_x - 100, start_y), color=(150, 150, 150), font_size=30) button_w = 200 btn_y1, btn_y2 = h - 140, h - 80 # 开始按钮 start_x1, start_x2 = center_x - button_w - 50, center_x - 50 cv2.rectangle(frame, (start_x1, btn_y1), (start_x2, btn_y2), (0, 180, 0), -1) cv2.rectangle(frame, (start_x1, btn_y1), (start_x2, btn_y2), (0, 255, 0), 3) frame = put_chinese_text(frame, "开始游戏", (center_x - button_w // 2 - 50, h - 100), color=(255, 255, 255), font_size=40) # 退出按钮 quit_x1, quit_x2 = center_x + 50, center_x + button_w + 50 cv2.rectangle(frame, (quit_x1, btn_y1), (quit_x2, btn_y2), (180, 0, 0), -1) cv2.rectangle(frame, (quit_x1, btn_y1), (quit_x2, btn_y2), (255, 0, 0), 3) frame = put_chinese_text(frame, "退出游戏", (center_x + button_w // 2 - 40, h - 100), color=(255, 255, 255), font_size=40) # 清空按钮 clear_x1, clear_x2 = center_x - 100, center_x + 100 cv2.rectangle(frame, (clear_x1, h - 70), (clear_x2, h - 10), (100, 100, 100), -1) cv2.rectangle(frame, (clear_x1, h - 70), (clear_x2, h - 10), (200, 200, 200), 2) frame = put_chinese_text(frame, "清空记录", (center_x - 45, h - 30), color=(255, 255, 255), font_size=32) frame = put_chinese_text(frame, "按'S'开始|'Q'退出|'C'清空", (center_x - 240, h - 160), color=(200, 200, 255), font_size=24) cv2.imshow("Hand-Controlled Snake Game", frame) while True: key = cv2.waitKey(1) & 0xFF if key in (ord('s'), ord('S')): return "start" elif key in (ord('q'), ord('Q'), 27): return "quit" elif key in (ord('c'), ord('C')): result = clear_ranking() return "refresh" # ------------------------------ # 初始化 MediaPipe Hands # ------------------------------ mp_hands = mp.solutions.hands mp_draw = mp.solutions.drawing_utils hands = mp_hands.Hands( static_image_mode=False, max_num_hands=1, min_detection_confidence=0.7, min_tracking_confidence=0.5 ) # ------------------------------ # 游戏参数设置 # ------------------------------ width, height = 1280, 720 snake_speed = 8 snake_length = 3 score = 0 game_over = False walls = [] cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) snake = deque([(width // 2, height // 2)]) direction = np.array([1.0, 0.0]) target_pos = None food_radius = 15 food_color = (0, 255, 0) food = [random.randint(50, width - 50), random.randint(50, height - 50)] # ------------------------------ # 工具函数 # ------------------------------ def is_point_in_wall(x, y, walls): return any(wx <= x <= wx + ww and wy <= y <= wy + wh for (wx, wy, ww, wh) in walls) def check_wall_collision(head, walls): hx, hy = head return any(wx <= hx <= wx + ww and wy <= hy <= wy + wh for (wx, wy, ww, wh) in walls) def generate_food(snake_body, walls): max_attempts = 100 min_dist = 30 for _ in range(max_attempts): x = random.randint(50, width - 50) y = random.randint(50, height - 50) pos = np.array([x, y]) if is_point_in_wall(x, y, walls): continue if any(np.linalg.norm(pos - np.array(seg)) < min_dist for seg in snake_body): continue return [x, y] return [width - 60, height - 60] # BFS 路径可达性检查(略去以保持简洁,实际项目建议保留) # 此处简化为仅距离判断(用于快速测试) def is_path_available(head_pos, food_pos, walls): # 简化版:只要不直接被墙堵死就算通路 return not is_point_in_wall(food_pos[0], food_pos[1], walls) def add_wall_safely(walls_list, snake_body, food_pos): if len(walls_list) >= 5: return walls_list for _ in range(30): ww = random.randint(40, 150) wh = random.randint(40, 150) wx = random.randint(50, width - ww - 50) wy = random.randint(50, height - wh - 50) new_wall = (wx, wy, ww, wh) temp_walls = walls_list + [new_wall] if not is_point_in_wall(snake_body[0][0], snake_body[0][1], temp_walls) and \ not is_point_in_wall(food_pos[0], food_pos[1], temp_walls): walls_list.append(new_wall) break return walls_list # ------------------------------ # 主游戏循环 # ------------------------------ while True: action = show_ranking_menu() if action == "quit": break elif action == "refresh": continue elif action == "start": # 重置游戏 snake.clear() snake.append((width // 2, height // 2)) direction = np.array([1.0, 0.0]) snake_length = 3 score = 0 game_over = False walls = [] target_pos = None food = generate_food(snake, walls) while cap.isOpened(): ret, frame = cap.read() if not ret: break frame = cv2.flip(frame, 1) rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 👇 安全调用 MediaPipe 并检查结果 results = hands.process(rgb_frame) new_target = None # ✅ 关键修复:先检查是否检测到手 if not game_over and results is not None: # ✅ 使用 hasattr 和 None 判断避免 IDE 警告 if hasattr(results, 'multi_hand_landmarks') and results.multi_hand_landmarks: for hand_landmarks in results.multi_hand_landmarks: mp_draw.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS) idx_tip = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP] x, y = int(idx_tip.x * width), int(idx_tip.y * height) # 更新目标点(平滑滤波) if target_pos is None: target_pos = (x, y) else: alpha = 0.3 target_pos = ( int(alpha * x + (1 - alpha) * target_pos[0]), int(alpha * y + (1 - alpha) * target_pos[1]) ) new_target = target_pos # 显示目标点(紫色圆圈) cv2.circle(frame, target_pos, 12, (255, 0, 255), -1) # 移动逻辑 if not game_over: head = np.array(snake[0]) if new_target is not None: to_target = np.array(new_target) - head dist = np.linalg.norm(to_target) if dist > 30: desired_dir = to_target / (dist + 1e-8) curr_dir_norm = direction / (np.linalg.norm(direction) + 1e-8) dot = np.clip(np.dot(curr_dir_norm, desired_dir), -1.0, 1.0) angle = np.arccos(dot) if angle > 0.25: # 最大转向弧度 w1 = np.sin(angle - 0.25) w2 = np.sin(0.25) sin_a = np.sin(angle) blended = (w1 * curr_dir_norm + w2 * desired_dir) / (sin_a + 1e-8) direction = blended / (np.linalg.norm(blended) + 1e-8) else: direction = desired_dir new_head = head + direction * snake_speed new_head = np.round(new_head).astype(int) snake.appendleft(tuple(new_head)) # 碰撞检测 if (check_wall_collision(new_head, walls) or new_head[0] <= 0 or new_head[0] >= width or new_head[1] <= 0 or new_head[1] >= height): game_over = True for i in range(4, len(snake)): seg = np.array(snake[i]) if np.linalg.norm(new_head - seg) < 15: game_over = True break # 吃食物 if np.linalg.norm(new_head - np.array(food)) < (food_radius + 10): old_score = score score += 1 food = generate_food(snake, walls) snake_length += 2 if score // 5 > old_score // 5: walls = add_wall_safely(walls, snake, food) while len(snake) > snake_length: snake.pop() # 绘图部分 for wall in walls: wx, wy, ww, wh = wall cv2.rectangle(frame, (wx, wy), (wx + ww, wy + wh), (255, 255, 0), -1) cv2.rectangle(frame, (wx, wy), (wx + ww, wy + wh), (0, 0, 0), 3) cv2.circle(frame, tuple(food), food_radius, food_color, -1) for i in range(1, len(snake)): alpha = i / len(snake) color = (0, int(255 * (1 - alpha)), int(100 * (1 - alpha))) cv2.line(frame, snake[i - 1], snake[i], color, 8) cv2.circle(frame, snake[0], 10, (255, 255, 255), -1) cv2.circle(frame, snake[0], 8, (0, 150, 0), -1) frame = put_chinese_text(frame, f"得分: {score}", (20, 15), color=(255, 255, 255), font_size=50) if game_over: overlay = frame.copy() cv2.rectangle(overlay, (width // 4, height // 4), (3 * width // 4, 3 * height // 4), (0, 0, 0), -1) cv2.addWeighted(overlay, 0.7, frame, 0.3, 0, frame) frame = put_chinese_text(frame, "游戏结束", (width // 2 - 120, height // 2 - 60), color=(0, 0, 255), font_size=60) frame = put_chinese_text(frame, "按任意键返回主菜单", (width // 2 - 160, height // 2), color=(255, 255, 255), font_size=36) frame = put_chinese_text(frame, f"最终得分: {score}", (width // 2 - 120, height // 2 + 50), color=(255, 255, 0), font_size=32) cv2.imshow("Hand-Controlled Snake Game", frame) cv2.waitKey(0) break cv2.imshow("Hand-Controlled Snake Game", frame) if cv2.waitKey(1) == 27: # ESC break # ------------------------------ # 释放资源 # ------------------------------ cap.release() cv2.destroyAllWindows() hands.close()
最新发布
11-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值