Kmin/php-raylib用户体验工具:用户测试与反馈收集
痛点:游戏开发中的用户反馈收集难题
还在为游戏开发中的用户反馈收集而烦恼吗?传统的日志系统难以捕捉游戏运行时的真实体验,用户反馈往往滞后且不准确。php-raylib 内置的强大日志和调试功能,让你能够构建专业的用户体验测试工具链,实时收集用户行为数据,为游戏优化提供数据支撑。
读完本文,你将获得:
- php-raylib 日志系统的完整使用方法
- 用户行为记录和性能监控的实现方案
- 自动化测试和反馈收集的最佳实践
- 可视化数据分析工具的开发技巧
php-raylib 日志系统深度解析
核心日志功能
php-raylib 提供了多层次的日志系统,支持从调试信息到错误报告的全方位日志记录:
use Kingbes\Raylib\Core;
// 设置日志级别(只记录警告及以上级别的日志)
Core::setTraceLogLevel(LOG_WARNING);
// 输出不同级别的日志信息
Core::traceLog(LOG_DEBUG, "调试信息:游戏初始化开始");
Core::traceLog(LOG_INFO, "游戏资源加载完成");
Core::traceLog(LOG_WARNING, "纹理尺寸超出预期:%dx%d", 2048, 2048);
Core::traceLog(LOG_ERROR, "严重错误:着色器编译失败");
日志级别说明
| 级别常量 | 数值 | 描述 | 使用场景 |
|---|---|---|---|
| LOG_ALL | 0 | 所有日志 | 开发调试阶段 |
| LOG_TRACE | 1 | 追踪信息 | 详细执行流程 |
| LOG_DEBUG | 2 | 调试信息 | 问题排查 |
| LOG_INFO | 3 | 一般信息 | 正常运行状态 |
| LOG_WARNING | 4 | 警告信息 | 潜在问题 |
| LOG_ERROR | 5 | 错误信息 | 功能异常 |
| LOG_FATAL | 6 | 严重错误 | 系统崩溃 |
| LOG_NONE | 7 | 无日志 | 生产环境 |
用户行为记录系统设计
基础行为记录类
class UserBehaviorRecorder {
private static $sessionId;
private static $events = [];
private static $startTime;
public static function init() {
self::$sessionId = uniqid('session_', true);
self::$startTime = microtime(true);
Core::setTraceLogCallback([self::class, 'logCallback']);
}
public static function logCallback($level, $message) {
$event = [
'timestamp' => microtime(true),
'level' => $level,
'message' => $message,
'session_id' => self::$sessionId,
'frame_count' => Core::getFPS() ?: 0
];
self::$events[] = $event;
// 实时发送到分析服务器(可选)
if ($level >= LOG_WARNING) {
self::sendToAnalytics($event);
}
}
public static function recordCustomEvent($eventName, $data = []) {
$event = [
'type' => 'custom',
'name' => $eventName,
'data' => $data,
'timestamp' => microtime(true),
'session_id' => self::$sessionId
];
self::$events[] = $event;
Core::traceLog(LOG_INFO, "用户事件: %s", $eventName);
}
public static function getSessionReport() {
return [
'session_id' => self::$sessionId,
'duration' => microtime(true) - self::$startTime,
'total_events' => count(self::$events),
'events' => self::$events
];
}
private static function sendToAnalytics($event) {
// 实现网络发送逻辑
}
}
游戏内事件记录示例
// 初始化记录器
UserBehaviorRecorder::init();
// 记录游戏关键事件
Core::initWindow(800, 600, "用户体验测试游戏");
Core::setTargetFPS(60);
$playerScore = 0;
$levelStartTime = Core::getTime();
while (!Core::windowShouldClose()) {
Core::beginDrawing();
Core::clearBackground(Utils::color(245, 245, 245));
// 记录用户输入
if (Core::isKeyPressed(KEY_SPACE)) {
UserBehaviorRecorder::recordCustomEvent('player_jump', [
'score' => $playerScore,
'level_time' => Core::getTime() - $levelStartTime
]);
}
// 记录游戏状态变化
if ($playerScore % 100 === 0 && $playerScore > 0) {
UserBehaviorRecorder::recordCustomEvent('score_milestone', [
'milestone' => $playerScore
]);
}
Core::endDrawing();
}
// 游戏结束时保存用户行为数据
$report = UserBehaviorRecorder::getSessionReport();
file_put_contents('user_session_' . date('Ymd_His') . '.json',
json_encode($report, JSON_PRETTY_PRINT));
性能监控与优化工具
实时性能监控面板
class PerformanceMonitor {
private static $frameTimes = [];
private static $maxSamples = 300;
private static $metrics = [];
public static function update() {
$currentTime = Core::getTime();
$frameTime = Core::getFrameTime();
// 记录帧时间
self::$frameTimes[] = $frameTime;
if (count(self::$frameTimes) > self::$maxSamples) {
array_shift(self::$frameTimes);
}
// 计算性能指标
self::$metrics = [
'fps' => Core::getFPS(),
'frame_time' => $frameTime,
'avg_frame_time' => array_sum(self::$frameTimes) / count(self::$frameTimes),
'max_frame_time' => max(self::$frameTimes),
'min_frame_time' => min(self::$frameTimes),
'frame_time_95th' => self::calculatePercentile(self::$frameTimes, 95),
'memory_usage' => memory_get_usage(true),
'memory_peak' => memory_get_peak_usage(true)
];
// 性能警告
if ($frameTime > 0.033) { // 低于30FPS
Core::traceLog(LOG_WARNING, "性能警告: 帧时间 %.3fms", $frameTime * 1000);
}
}
public static function drawMetrics() {
$metrics = self::$metrics;
$x = 10;
$y = 10;
$lineHeight = 20;
Text::drawText(sprintf("FPS: %d", $metrics['fps']), $x, $y, 16, Utils::color(0, 0, 0));
Text::drawText(sprintf("帧时间: %.2fms", $metrics['frame_time'] * 1000), $x, $y + $lineHeight, 16, Utils::color(0, 0, 0));
Text::drawText(sprintf("内存: %.1fMB", $metrics['memory_usage'] / 1024 / 1024), $x, $y + $lineHeight * 2, 16, Utils::color(0, 0, 0));
// 绘制帧时间图表
self::drawFrameTimeGraph(150, 10, 200, 60);
}
private static function drawFrameTimeGraph($x, $y, $width, $height) {
$maxFrameTime = max(self::$frameTimes) ?: 0.05;
$sampleCount = count(self::$frameTimes);
// 绘制背景
Shapes::drawRectangle($x, $y, $width, $height, Utils::color(240, 240, 240));
Shapes::drawRectangleLines($x, $y, $width, $height, Utils::color(200, 200, 200));
// 绘制帧时间曲线
$prevX = $x;
$prevY = $y + $height;
foreach (self::$frameTimes as $i => $frameTime) {
$pointX = $x + ($i / $sampleCount) * $width;
$pointY = $y + $height - ($frameTime / $maxFrameTime) * $height;
if ($i > 0) {
Shapes::drawLine($prevX, $prevY, $pointX, $pointY, Utils::color(255, 0, 0));
}
$prevX = $pointX;
$prevY = $pointY;
}
// 绘制参考线(16.67ms = 60FPS)
$refY = $y + $height - (0.01667 / $maxFrameTime) * $height;
Shapes::drawLine($x, $refY, $x + $width, $refY, Utils::color(0, 255, 0));
}
private static function calculatePercentile($values, $percentile) {
sort($values);
$index = ($percentile / 100) * (count($values) - 1);
if (floor($index) == $index) {
return $values[$index];
} else {
$lower = $values[floor($index)];
$upper = $values[ceil($index)];
return $lower + ($upper - $lower) * ($index - floor($index));
}
}
public static function getMetrics() {
return self::$metrics;
}
}
性能监控集成示例
// 在主循环中集成性能监控
while (!Core::windowShouldClose()) {
PerformanceMonitor::update();
Core::beginDrawing();
Core::clearBackground(Utils::color(245, 245, 245));
// 游戏逻辑...
// 显示性能监控面板
if (Core::isKeyDown(KEY_F3)) {
PerformanceMonitor::drawMetrics();
}
Core::endDrawing();
}
自动化测试框架
基于场景的自动化测试
class AutomatedTester {
private static $testCases = [];
private static $currentTest = null;
private static $testResults = [];
public static function addTestCase($name, $setup, $test, $teardown = null) {
self::$testCases[$name] = [
'setup' => $setup,
'test' => $test,
'teardown' => $teardown
];
}
public static function runAllTests() {
foreach (self::$testCases as $name => $testCase) {
self::runTest($name, $testCase);
}
return self::generateTestReport();
}
private static function runTest($name, $testCase) {
Core::traceLog(LOG_INFO, "开始测试: %s", $name);
try {
// 执行setup
if ($testCase['setup']) {
call_user_func($testCase['setup']);
}
// 执行测试
$result = call_user_func($testCase['test']);
// 执行teardown
if ($testCase['teardown']) {
call_user_func($testCase['teardown']);
}
self::$testResults[$name] = [
'status' => 'passed',
'result' => $result,
'timestamp' => date('Y-m-d H:i:s')
];
Core::traceLog(LOG_INFO, "测试通过: %s", $name);
} catch (Exception $e) {
self::$testResults[$name] = [
'status' => 'failed',
'error' => $e->getMessage(),
'timestamp' => date('Y-m-d H:i:s')
];
Core::traceLog(LOG_ERROR, "测试失败: %s - %s", $name, $e->getMessage());
}
}
private static function generateTestReport() {
$passed = 0;
$failed = 0;
foreach (self::$testResults as $result) {
if ($result['status'] === 'passed') {
$passed++;
} else {
$failed++;
}
}
return [
'summary' => [
'total' => count(self::$testResults),
'passed' => $passed,
'failed' => $failed,
'success_rate' => $passed / count(self::$testResults) * 100
],
'details' => self::$testResults
];
}
}
测试用例示例
// 定义性能测试用例
AutomatedTester::addTestCase(
'渲染性能测试',
function() {
// 初始化测试场景
Core::initWindow(800, 600, "性能测试");
Core::setTargetFPS(0); // 不限帧率
},
function() {
$frameTimes = [];
$testDuration = 5; // 测试5秒
$startTime = Core::getTime();
while (Core::getTime() - $startTime < $testDuration) {
$frameStart = Core::getTime();
Core::beginDrawing();
Core::clearBackground(Utils::color(255, 255, 255));
// 绘制测试内容
for ($i = 0; $i < 1000; $i++) {
Shapes::drawRectangle(
rand(0, 800),
rand(0, 600),
10, 10,
Utils::color(rand(0, 255), rand(0, 255), rand(0, 255))
);
}
Core::endDrawing();
$frameTimes[] = Core::getTime() - $frameStart;
}
// 计算性能指标
$avgFrameTime = array_sum($frameTimes) / count($frameTimes);
$avgFPS = 1 / $avgFrameTime;
return [
'avg_frame_time' => $avgFrameTime,
'avg_fps' => $avgFPS,
'min_fps' => 1 / max($frameTimes),
'max_fps' => 1 / min($frameTimes),
'frame_count' => count($frameTimes)
];
},
function() {
Core::closeWindow();
}
);
// 运行所有测试
$report = AutomatedTester::runAllTests();
file_put_contents('performance_report.json', json_encode($report, JSON_PRETTY_PRINT));
用户反馈收集系统
游戏内反馈界面
class FeedbackSystem {
private static $feedbackData = [];
private static $isActive = false;
private static $currentPage = 'main';
private static $inputText = '';
private static $rating = 5;
public static function toggle() {
self::$isActive = !self::$isActive;
if (self::$isActive) {
self::$inputText = '';
self::$rating = 5;
self::$currentPage = 'main';
}
}
public static function isActive() {
return self::$isActive;
}
public static function update() {
if (!self::$isActive) return;
// 处理键盘输入
$key = Core::getCharPressed();
if ($key >= 32 && $key <= 125) { // 可打印字符
self::$inputText .= chr($key);
} elseif (Core::isKeyPressed(KEY_BACKSPACE) && strlen(self::$inputText) > 0) {
self::$inputText = substr(self::$inputText, 0, -1);
}
// 处理评分调整
if (Core::isKeyPressed(KEY_UP) && self::$rating < 5) {
self::$rating++;
} elseif (Core::isKeyPressed(KEY_DOWN) && self::$rating > 1) {
self::$rating--;
}
// 提交反馈
if (Core::isKeyPressed(KEY_ENTER) && !empty(self::$inputText)) {
self::submitFeedback();
self::$isActive = false;
}
// 取消反馈
if (Core::isKeyPressed(KEY_ESCAPE)) {
self::$isActive = false;
}
}
public static function draw() {
if (!self::$isActive) return;
// 绘制反馈界面背景
Shapes::drawRectangle(100, 100, 600, 400, Utils::color(50, 50, 50, 200));
Shapes::drawRectangleLines(100, 100, 600, 400, Utils::color(255, 255, 255));
// 绘制标题
Text::drawText("用户反馈", 120, 120, 24, Utils::color(255, 255, 255));
// 绘制评分
Text::drawText("评分:", 120, 160, 20, Utils::color(255, 255, 255));
for ($i = 1; $i <= 5; $i++) {
$color = $i <= self::$rating ? Utils::color(255, 215, 0) : Utils::color(100, 100, 100);
Shapes::drawRectangle(180 + $i * 30, 160, 25, 25, $color);
}
// 绘制输入框
Text::drawText("反馈内容:", 120, 200, 20, Utils::color(255, 255, 255));
Shapes::drawRectangle(120, 230, 560, 150, Utils::color(30, 30, 30));
Shapes::drawRectangleLines(120, 230, 560, 150, Utils::color(200, 200, 200));
// 绘制输入文本(自动换行)
$lines = self::wrapText(self::$inputText, 550, 20);
foreach ($lines as $i => $line
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



