游戏时间
游戏时间系统完全指南 | 开发进度追踪
《蛙噻!好摊》采用独特的时间系统,将现实时间与游戏时间相结合。理解游戏时间机制对于合理安排经营策略、把握市场时机至关重要。
时间系统概览
游戏时间与现实时间的换算关系:
现实时间
8
小时
游戏时间
1
天
时间比例
1:3
倍速
| 现实时间 | 游戏时间 | 说明 |
|---|---|---|
| 8小时 | 1天 | 完成一个完整的日夜循环 |
| 24小时 | 3天 | 约等于游戏内三天 |
| 56小时 | 7天 | 完成一个季节周期 |
| 224小时(约9天) | 28天 | 完成一个完整的月周期 |
日夜循环
每个游戏日分为六个时段,不同时段有不同的经营特点:
深夜
清晨/傍晚
上午
正午
夜晚
| 时段 | 游戏时间 | 现实时间 | 客流加成 |
|---|---|---|---|
| 深夜 | 23:00-03:00 | 约40分钟 | -50% |
| 黎明 | 03:00-07:00 | 约40分钟 | +20% |
| 上午 | 07:00-11:00 | 约40分钟 | +30% |
| 正午 | 11:00-15:00 | 约40分钟 | +10% |
| 傍晚 | 15:00-19:00 | 约40分钟 | +35% |
| 夜晚 | 19:00-23:00 | 约40分钟 | +25% |
季节系统
游戏内每7个游戏日(约56小时现实时间)为一个季节:
春季
- 种子、农具需求增加
- 药材价格下降
夏季
- 消暑商品热销
- 防具需求增加
秋季
- 收获季节,粮食充足
- 月饼、礼品热销
冬季
- 保暖用品需求增加
- 年货采购高峰
离线时间
| 离线时长 | 收益效率 | 说明 |
|---|---|---|
| 0-4小时 | 100% | 全额收益 |
| 4-8小时 | 80% | 收益略微下降 |
| 8-12小时 | 60% | 收益明显下降 |
| 12-24小时 | 40% | 收益大幅下降 |
| 超过24小时 | 停止 | 不再累积收益 |
游戏时间系统开发流程
按照以下计划逐步完成开发,完成所有计划即开发成功。
第一阶段:基础时间框架
-
任务1.1:创建时间管理类 GameTime
创建 js/game-time.js 文件,定义 GameTime 类,包含基础属性和方法
class GameTime { constructor() { this.realStartTime = Date.now(); // 现实开始时间 this.gameStartTime = 0; // 游戏开始时间(毫秒) this.timeRatio = 3; // 时间比例:现实1秒 = 游戏3秒 this.paused = false; // 是否暂停 } // 获取当前游戏时间(毫秒) getGameTime() { if (this.paused) return this.gameStartTime; const elapsed = Date.now() - this.realStartTime; return this.gameStartTime + elapsed * this.timeRatio; } // 获取游戏时间详情 getTimeDetails() { const gameTime = this.getGameTime(); return { totalMs: gameTime, day: Math.floor(gameTime / (8 * 60 * 60 * 1000)), // 游戏天数 hour: Math.floor((gameTime % (8 * 60 * 60 * 1000)) / (60 * 60 * 1000 / 3)), minute: Math.floor((gameTime % (60 * 60 * 1000 / 3)) / (60 * 1000 / 3)) }; } } -
任务1.2:实现时间换算函数
现实8小时 = 游戏1天,实现双向换算
// 现实时间转游戏时间 function realToGame(realMs) { // 现实8小时 = 游戏24小时 // 现实1秒 = 游戏3秒 return realMs * 3; } // 游戏时间转现实时间 function gameToReal(gameMs) { return gameMs / 3; } // 获取游戏内时间(小时:分钟) function getGameClock(gameMs) { const dayMs = 8 * 60 * 60 * 1000; // 现实8小时为一天 const gameDayMs = gameMs % dayMs; const hour = Math.floor(gameDayMs / (dayMs / 24)); const minute = Math.floor((gameDayMs % (dayMs / 24)) / (dayMs / 24 / 60)); return { hour, minute }; } -
任务1.3:创建时间显示UI组件
在页面右上角显示当前游戏时间
// HTML <div id="game-clock"> <span id="game-day">第1天</span> <span id="game-time">08:00</span> </div> // CSS #game-clock { position: fixed; top: 10px; right: 10px; background: rgba(0,0,0,0.7); color: white; padding: 8px 16px; border-radius: 4px; font-family: monospace; } // JS更新 function updateClock() { const details = gameTime.getTimeDetails(); document.getElementById('game-day').textContent = '第' + (details.day + 1) + '天'; document.getElementById('game-time').textContent = String(details.hour).padStart(2, '0') + ':' + String(details.minute).padStart(2, '0'); } setInterval(updateClock, 1000);
验证方法
打开页面,观察时间显示是否正常跳动,每现实1秒,游戏时间应前进3秒。
第二阶段:日夜循环系统
-
任务2.1:定义时段数据
定义六个时段及其属性
const TIME_PERIODS = [ { name: '深夜', start: 23, end: 3, trafficBonus: -0.5, bgColor: '#1a237e' }, { name: '黎明', start: 3, end: 7, trafficBonus: 0.2, bgColor: '#ff9800' }, { name: '上午', start: 7, end: 11, trafficBonus: 0.3, bgColor: '#ffc107' }, { name: '正午', start: 11, end: 15, trafficBonus: 0.1, bgColor: '#ffeb3b' }, { name: '傍晚', start: 15, end: 19, trafficBonus: 0.35, bgColor: '#e65100' }, { name: '夜晚', start: 19, end: 23, trafficBonus: 0.25, bgColor: '#4a148c' } ]; function getCurrentPeriod(hour) { for (let period of TIME_PERIODS) { if (period.start > period.end) { // 跨午夜时段 if (hour >= period.start || hour < period.end) return period; } else { if (hour >= period.start && hour < period.end) return period; } } return TIME_PERIODS[0]; } -
任务2.2:实现时段切换事件
当时段变化时触发事件
class GameTime { constructor() { // ... 其他属性 this.currentPeriod = null; this.onPeriodChange = null; // 时段变化回调 } update() { const details = this.getTimeDetails(); const newPeriod = getCurrentPeriod(details.hour); if (this.currentPeriod !== newPeriod) { this.currentPeriod = newPeriod; if (this.onPeriodChange) { this.onPeriodChange(newPeriod); } } } } // 使用示例 gameTime.onPeriodChange = function(period) { console.log('进入' + period.name + '时段'); // 更新UI背景色 document.body.style.backgroundColor = period.bgColor; }; -
任务2.3:实现客流加成计算
根据时段返回客流加成
function getTrafficBonus() { const details = gameTime.getTimeDetails(); const period = getCurrentPeriod(details.hour); return period.trafficBonus; } // 应用客流加成 function calculateCustomerCount(baseCount) { const bonus = getTrafficBonus(); return Math.floor(baseCount * (1 + bonus)); }
验证方法
观察页面背景色是否随时段变化,客流加成是否正确计算。
第三阶段:季节系统
-
任务3.1:定义季节数据
四季循环,每7游戏日换季
const SEASONS = [ { name: '春季', color: '#4caf50', priceModifier: { food: 1.0, medicine: 0.85, equipment: 1.0 } }, { name: '夏季', color: '#ff9800', priceModifier: { food: 1.1, medicine: 1.0, equipment: 1.1 } }, { name: '秋季', color: '#795548', priceModifier: { food: 0.8, medicine: 1.1, equipment: 1.0 } }, { name: '冬季', color: '#2196f3', priceModifier: { food: 1.15, medicine: 1.2, equipment: 1.15 } } ]; const DAYS_PER_SEASON = 7; // 每7个游戏日换季 function getCurrentSeason(gameDay) { const seasonIndex = Math.floor(gameDay / DAYS_PER_SEASON) % 4; return SEASONS[seasonIndex]; } -
任务3.2:实现季节价格修正
根据季节调整商品价格
function getSeasonalPrice(basePrice, itemType) { const details = gameTime.getTimeDetails(); const season = getCurrentSeason(details.day); const modifier = season.priceModifier[itemType] || 1.0; return Math.floor(basePrice * modifier); } -
任务3.3:实现季节变化事件
换季时触发通知
class GameTime { constructor() { this.currentSeason = null; this.onSeasonChange = null; } update() { const details = this.getTimeDetails(); const newSeason = getCurrentSeason(details.day); if (this.currentSeason !== newSeason) { this.currentSeason = newSeason; if (this.onSeasonChange) { this.onSeasonChange(newSeason); } } } }
验证方法
模拟时间快速前进,观察季节是否每7天切换一次,价格是否正确调整。
第四阶段:离线时间处理
-
任务4.1:保存最后在线时间
使用 localStorage 保存
function saveLastOnlineTime() { localStorage.setItem('lastOnlineTime', Date.now().toString()); } function loadLastOnlineTime() { const saved = localStorage.getItem('lastOnlineTime'); return saved ? parseInt(saved) : Date.now(); } function calculateOfflineTime() { const lastOnline = loadLastOnlineTime(); const now = Date.now(); return now - lastOnline; } -
任务4.2:计算离线收益
根据离线时长计算收益,最长24小时
const OFFLINE_EFFICIENCY = [ { maxHours: 4, efficiency: 1.0 }, { maxHours: 8, efficiency: 0.8 }, { maxHours: 12, efficiency: 0.6 }, { maxHours: 24, efficiency: 0.4 } ]; function calculateOfflineEarnings(stall) { const offlineMs = Math.min(calculateOfflineTime(), 24 * 60 * 60 * 1000); const offlineHours = offlineMs / (60 * 60 * 1000); let efficiency = 0.4; for (let tier of OFFLINE_EFFICIENCY) { if (offlineHours <= tier.maxHours) { efficiency = tier.efficiency; break; } } // 计算基础收益 let baseEarnings = 0; stall.listedItems.forEach(item => { const avgSellTime = 2 * 60 * 60 * 1000; // 平均2小时卖出 const expectedSales = offlineMs / avgSellTime; baseEarnings += item.price * expectedSales; }); return Math.floor(baseEarnings * efficiency); } -
任务4.3:显示离线结算界面
玩家上线时显示离线收益
function showOfflineSummary() { const offlineTime = calculateOfflineTime(); if (offlineTime < 5 * 60 * 1000) return; // 少于5分钟不显示 const earnings = calculateOfflineEarnings(player.currentStall); // 显示弹窗 const modal = document.createElement('div'); modal.className = 'offline-modal'; modal.innerHTML = ` <h2>欢迎回来!</h2> <p>您离线了 ${formatTime(offlineTime)}</p> <p>离线收益:${earnings} 文</p> <button onclick="this.parentElement.remove()">领取</button> `; document.body.appendChild(modal); // 发放收益 player.money += earnings; }
验证方法
关闭页面后等待几分钟重新打开,检查是否正确显示离线收益。
第五阶段:定时事件系统
-
任务5.1:定义定时事件
每日事件和周期性事件
const DAILY_EVENTS = [ { name: '早市刷新', gameHour: 5, action: 'refreshMarket' }, { name: '商会任务', gameHour: 0, action: 'refreshQuests' } ]; const PERIODIC_EVENTS = [ { name: '集市大集', interval: 7, unit: 'days', action: 'startFair' }, { name: '季节更替', interval: 7, unit: 'days', action: 'changeSeason' }, { name: '物价重置', interval: 28, unit: 'days', action: 'resetPrices' } ]; -
任务5.2:实现事件调度器
检查并触发事件
class EventScheduler { constructor(gameTime) { this.gameTime = gameTime; this.lastCheckedDay = -1; this.lastCheckedHour = -1; } check() { const details = this.gameTime.getTimeDetails(); // 检查每日事件 if (details.hour !== this.lastCheckedHour) { this.lastCheckedHour = details.hour; DAILY_EVENTS.forEach(event => { if (event.gameHour === details.hour) { this.triggerEvent(event); } }); } // 检查周期性事件 if (details.day !== this.lastCheckedDay) { this.lastCheckedDay = details.day; PERIODIC_EVENTS.forEach(event => { if (details.day % event.interval === 0) { this.triggerEvent(event); } }); } } triggerEvent(event) { console.log('触发事件:' + event.name); // 根据event.action执行对应逻辑 } } -
任务5.3:集成到游戏循环
每秒检查事件
const scheduler = new EventScheduler(gameTime); function gameLoop() { gameTime.update(); scheduler.check(); updateClock(); } setInterval(gameLoop, 1000);
验证方法
模拟时间快速前进,观察事件是否在正确时间触发。
第六阶段:数据持久化
-
任务6.1:保存游戏时间状态
保存到 localStorage
function saveGameTime() { const data = { realStartTime: gameTime.realStartTime, gameStartTime: gameTime.gameStartTime, paused: gameTime.paused }; localStorage.setItem('gameTime', JSON.stringify(data)); } function loadGameTime() { const saved = localStorage.getItem('gameTime'); if (saved) { const data = JSON.parse(saved); gameTime.realStartTime = data.realStartTime; gameTime.gameStartTime = data.gameStartTime; gameTime.paused = data.paused; } } // 页面关闭时保存 window.addEventListener('beforeunload', saveGameTime); // 页面加载时恢复 window.addEventListener('load', loadGameTime); -
任务6.2:实现时间加速(调试用)
开发调试时可加速时间
// 调试模式下加速时间 const DEBUG_MODE = true; const DEBUG_SPEED = 60; // 60倍速 class GameTime { constructor() { this.timeRatio = DEBUG_MODE ? 3 * DEBUG_SPEED : 3; } }
验证方法
刷新页面后检查时间是否继续,不会重置。
完成清单
完成以下所有任务即表示游戏时间系统开发成功:
| 阶段 | 任务数 | 状态 |
|---|---|---|
| 第一阶段:基础时间框架 | 3 | 待完成 |
| 第二阶段:日夜循环系统 | 3 | 待完成 |
| 第三阶段:季节系统 | 3 | 待完成 |
| 第四阶段:离线时间处理 | 3 | 待完成 |
| 第五阶段:定时事件系统 | 3 | 待完成 |
| 第六阶段:数据持久化 | 2 | 待完成 |
| 总计 | 17 | 0/17 完成 |
开发提示
- 建议按顺序完成各阶段,后续阶段依赖前面的基础
- 每完成一个任务,在代码中标记为已完成
- 使用调试模式快速验证时间相关功能
- 注意处理跨午夜的时间计算