每天一个API(二)执行顺序

本文详细介绍了Unity中十个常用生命周期函数的作用及调用时机,包括Reset、Awake、OnEnable、Start、FixedUpdate、Update、LateUpdate、OnGUI、OnDestroy和OnDisable。这些函数对于Unity游戏开发至关重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Reset

Reset是在用户点击检视面板的Reset按钮或者首次添加该组件时被调用。此函数只在编辑模式下被调用。Reset最常用于在检视面板中给定一个最常用的默认值。(注意只在编辑模式下被调用,很奇葩有没有)。

二、Awake

Awake用于在游戏开始之前初始化变量或游戏状态。在脚本整个生命周期内它仅被调用一次.Awake在所有对象被初始化之后调用,所以你可以安全的与其他对象对话或用诸如 GameObject.FindWithTag 这样的函数搜索它们。每个游戏物体上的Awake以随机的顺序被调用。因此,你应该用Awake来设置脚本间的引用,并用Start来传递信息Awake总是在Start之前被调用。它不能用来执行协同程序。(不同游戏上的Awake随机调用,这是个大坑哦,小心小心)

三、OnEnable

OnEnable当对象变为可用或激活状态时此函数被调用。OnEnable不能用于协同程序。

四、Start

Start仅在Update函数第一次被调用前调用。Start在behaviour的生命周期中只被调用一次。它和Awake的不同是Start只在脚本实例被启用时调用。你可以按需调整延迟初始化代码。Awake总是在Start之前执行。这允许你协调初始化顺序。

五、FixedUpdate

当MonoBehaviour启用时,其 FixedUpdate 在每一帧被调用,可能是一次或者是多次

六、Update

当MonoBehaviour启用时,其Update在每一帧被调用。Update是实现各种游戏行为最常用的函数。

七、LateUpdate

LateUpdate:LateUpdate同样也是每一帧调用一次,在Update函数结束之后被调用。当物体在Update里移动时,跟随物体的相机可以在LateUpdate里实现。

八、OnGUI

这意味着你的OnGUI程序将会在每一帧被调用。如果Monobehaviour的enabled属性设为false,OnGUI()将不会被调用。

九、OnDestroy

当MonoBehaviour将被销毁时,这个函数被调用。

十、OnDisable

当对象变为不可用或非激活状态时此函数被调用。当物体被销毁时它将被调用,并且可用于任意清理代码。脚本被卸载时,OnDisable将被调用。

总结

大概常用的就这10个吧,其实常用的都没有10个,可能是我功力还不够深厚,,,就这样啦,大家有什么补充可以评论区告诉我哟

<think>我们计划使用一个定时器来检查当前时间,当时间达到凌晨4点时,执行重启操作。 重启操作包括关闭当前程序并启动一个新的实例。我们可以使用Windows命令行命令来实现,例如使用`cmd /c`来执行一系列命令。 注意:在Windows中,我们可以使用`taskkill`来结束当前进程,然后使用`start`来启动新的程序实例。但是,由于我们正在结束当前进程,所以需要小心地安排命令的顺序。 步骤: 1. 在程序中添加一个定时器(例如TTimer),设置其Interval为较短的时间(如60000毫秒,即1分钟)以每分钟检查一次时间。 2. 在定时器的OnTimer事件中,检查当前时间是否在凌晨4点附近(考虑到定时器检查的间隔,我们可以判断当前时间是否在4:00到4:01之间,或者更精确地判断小时和分钟)。 3. 当检测到凌晨4点时,执行重启命令。 重启命令的编写: 我们可以使用ShellExecute或CreateProcess来执行一个批处理命令,但更简单的方法是使用cmd命令: cmd /c ping 127.0.0.1 -n 2 > nul & taskkill /f /im 程序名.exe & start "" "程序路径.exe" 但是,注意我们不能立即杀死当前进程,因为那样会中断命令的执行。因此,我们可以先启动一个新的实例,然后关闭当前实例。或者,我们可以写一个单独的批处理文件来延时执行任务。 另一种方法是:先启动一个新的实例,然后关闭当前程序。这样更安全。 具体步骤(在检测到凌晨4点时): a. 启动程序的新实例(使用相同的可执行文件路径)。 b. 关闭当前程序。 注意:为了避免在4点整的时候多个检查点(比如每分钟检查一次)导致多次重启,我们需要设置一个标志,确保每天只重启一次。 实现: 1. 获取当前时间,判断小时是否为4,分钟是否为0(因为每分钟检查一次,所以我们可以设定在4:00:00到4:00:59之间触发,但实际定时器触发可能稍有延迟,所以判断分钟为0即可,并且我们确保一天只执行一次)。 2. 使用一个变量(例如LastRestartDate)记录上次重启的日期,如果当前日期与上次重启日期不同,并且时间是凌晨4点,则执行重启。 代码示例: 在窗体上放置一个Timer,设置Interval=60000(1分钟)。 在Timer事件中: ``` procedure TForm1.Timer1Timer(Sender: TObject); var CurrentTime: TDateTime; Hour, Min, Sec, MSec: Word; CurrentDate: TDate; begin CurrentTime := Now; DecodeTime(CurrentTime, Hour, Min, Sec, MSec); // 如果时间是凌晨4点整(我们取分钟为0,即4:00:00到4:00:59之间) if (Hour = 4) and (Min = 0) then begin CurrentDate := Date; // 检查今天是否已经重启过 if CurrentDate <> LastRestartDate then begin // 标记今天已经重启 LastRestartDate := CurrentDate; // 执行重启:启动新实例,然后关闭当前程序 RestartApplication; end; end; end; ``` 其中,RestartApplication过程可以这样写: ``` procedure TForm1.RestartApplication; var ExePath: string; begin ExePath := Application.ExeName; // 启动新实例 WinExec(PAnsiChar(AnsiString(ExePath)), SW_SHOW); // 关闭当前程序 Application.Terminate; end; ``` 注意:WinExec是较老的API,也可以使用ShellExecute或CreateProcess。这里为了简单使用WinExec。 但是,使用Application.Terminate会立即终止程序,而新启动的程序可能还没有完全启动。不过,由于是顺序执行,启动新程序后当前程序就会终止,所以通常没有问题。 另外,为了避免在重启时出现多个实例同时运行(比如原程序还没有退出,新程序已经启动),如果程序不支持多实例,我们需要在程序启动时检查是否已有实例运行。但重启时,我们允许先启动新实例再关闭旧实例,所以旧实例应该允许新实例启动后再退出。因此,在程序启动代码中,我们需要使用互斥来防止同时运行多个实例,但要注意在重启过程中,旧实例还没有退出,新实例启动时可能会被检测到已有实例而退出。所以我们需要在重启时临时允许两个实例同时存在。 另一种思路:我们可以让新实例启动后,旧实例再退出。这样在启动新实例时,旧实例仍然存在,所以新实例启动后应该等待旧实例退出(或者旧实例立即退出)?这取决于程序的设计。 如果我们希望程序只允许一个实例,那么重启时就会遇到问题:新实例启动时,旧实例还在运行,所以新实例会检测到已有实例,然后退出。这样重启就失败了。 因此,我们需要在重启时绕过单实例检查。可以在重启时传递一个命令行参数,告诉新实例这是一个重启操作,允许它等待旧实例退出后再继续。 但这样会增加复杂度。为了简化,我们可以这样处理: 1. 在重启时,先启动新实例,然后旧实例退出。 2. 在新实例启动时,检查是否有旧实例,如果有,则等待旧实例退出(例如等待几秒),然后再继续运行。或者,新实例启动后,如果检测到旧实例,则等待旧实例退出,然后自己继续运行。 但是,等待旧实例退出可能比较复杂。我们可以采用另一种方法:使用一个批处理文件来重启。 批处理文件内容(restart.bat): ``` @echo off :loop tasklist | find "程序名.exe" > nul if not errorlevel 1 ( timeout /t 1 > nul goto loop ) start "" "程序路径.exe" ``` 然后,在程序中执行这个批处理文件,然后退出。这样批处理会等待当前程序退出后再启动新程序。 但是,这样需要额外写一个批处理文件,并且需要知道程序路径。 为了简单,我们假设程序允许在短时间内有两个实例同时运行(比如重启时),或者我们的程序支持多实例运行(但通常应用程序都是单实例的)。所以我们需要解决单实例问题。 建议:在重启时,我们通过命令行参数告诉新实例这是重启启动的,然后新实例在启动后等待旧实例退出(通过查找窗口或者进程名)?或者,我们可以在程序退出时释放一个互斥量,新实例在启动时等待这个互斥量。 由于这个问题比较复杂,我们可以采用另一种方法:使用Windows的“任务计划程序”来定时启动程序。这样就不需要在程序内部实现重启了。但是,用户要求是在程序中实现。 因此,我们折中:在重启时,我们启动新实例,并传递一个命令行参数,例如“/restart”,然后旧实例退出。新实例在启动时,如果检测到有旧实例(通过互斥量),并且自己带有“/restart”参数,则等待旧实例退出(等待几秒钟),然后再继续。这样,新实例启动后,旧实例会退出,新实例等待旧实例退出后,再继续运行(此时旧实例已经退出,互斥量被释放,新实例就可以正常运行了)。 但是,这需要修改程序的启动代码,而且相对复杂。 为了快速实现,我们假设程序允许同时运行多个实例。那么上面的RestartApplication方法就可以直接使用。 如果程序必须是单实例的,那么我们需要在程序启动时检查实例,但重启时允许两个实例短暂共存。我们可以这样修改启动代码: 在工程文件中,创建互斥量之前,先检查命令行参数。如果命令行参数包含“/restart”,则等待一段时间(比如5秒)再检查互斥量,这样旧实例就有时间退出了。 工程文件示例(.dpr): ``` program Project1; uses Forms, Windows, SysUtils, Unit1 in 'Unit1.pas' {Form1}; {$R *.res} var Mutex: THandle; WaitResult: DWORD; begin // 检查命令行参数,如果有/restart,则等待5秒 if (ParamCount > 0) and (SameText(ParamStr(1), '/restart')) then Sleep(5000); // 等待5秒,让旧实例退出 Mutex := CreateMutex(nil, True, 'MyApplicationUniqueMutex'); WaitResult := WaitForSingleObject(Mutex, 0); if WaitResult <> WAIT_OBJECT_0 then begin MessageBox(0, '程序已经运行!', '提示', MB_OK or MB_ICONINFORMATION); Exit; end; Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end. ``` 然后,在重启时,我们这样启动新实例: ``` procedure TForm1.RestartApplication; var ExePath: string; begin ExePath := Application.ExeName; // 启动新实例,并传递参数/restart ShellExecute(0, 'open', PChar(ExePath), '/restart', nil, SW_SHOW); // 关闭当前程序 Application.Terminate; end; ``` 这样,新实例启动时带有参数/restart,会等待5秒,让旧实例(当前程序)退出。然后新实例再检查互斥量,此时旧实例已经退出,所以新实例可以正常运行。 注意:等待5秒可能不够,如果旧实例退出需要更长时间,可以适当延长。但通常5秒足够。 总结: 1. 在定时器事件中检查时间是否为凌晨4点,并且今天还没有重启过。 2. 重启时,使用ShellExecute启动新实例,并传递参数“/restart”,然后当前程序退出。 3. 在新实例的工程文件中,如果检测到“/restart”参数,则等待5秒再检查互斥量,以避免与正在退出的旧实例冲突。 这样,我们就实现了每天凌晨4点自动重启程序的功能。 注意:这种方法在程序退出时可能会强制终止,如果有未保存的数据,需要谨慎。因此,最好在重启前确保程序状态是安全的。 另外,如果程序在4点整时没有运行,那么就不会触发重启。如果希望即使程序没有运行也要在凌晨4点启动程序,那么需要使用操作系统的任务计划。但题目要求是在程序中实现,所以这里只考虑程序已经在运行的情况。 如果要求程序未运行时也要启动,那么就需要使用任务计划程序了。但题目要求是程序自动重启,所以应该是程序已经在运行的情况下。 因此,我们按照上述方法实现。 代码示例(部分): 定时器事件和重启过程如上所述。 工程文件修改如上所述。 注意:工程文件中创建互斥量的代码需要调整,确保在等待后再次检查互斥量。 但是,上面的工程文件代码在等待5秒后并没有再次等待互斥量,而是直接创建了互斥量并尝试获取。实际上,在等待5秒后,旧实例应该已经退出,所以互斥量会被释放,新实例可以成功获取。但是,如果5秒后旧实例还没有退出(比如程序关闭需要时间),那么新实例仍然会失败。我们可以循环等待直到成功,但这样可能会死循环。所以,我们可以在等待5秒后,再尝试获取一次,如果还是失败,就退出。 修改工程文件: ``` // 检查命令行参数,如果有/restart,则等待5秒 if (ParamCount > 0) and (SameText(ParamStr(1), '/restart')) then begin // 等待5秒,让旧实例退出 Sleep(5000); end; Mutex := CreateMutex(nil, True, 'MyApplicationUniqueMutex'); if GetLastError = ERROR_ALREADY_EXISTS then begin // 如果互斥量已经存在,说明另一个实例正在运行 CloseHandle(Mutex); MessageBox(0, '程序已经运行!', '提示', MB_OK or MB_ICONINFORMATION); Exit; end; ``` 实际上,我们使用CreateMutex创建互斥量,如果已经存在,则GetLastError会返回ERROR_ALREADY_EXISTS。我们不需要WaitForSingleObject,因为CreateMutex已经创建了互斥量,如果已经存在,我们就关闭句柄并退出。 所以,工程文件可以这样写: ``` var Mutex: THandle; begin // 检查命令行参数,如果有/restart,则等待5秒 if (ParamCount > 0) and (SameText(ParamStr(1), '/restart')) then Sleep(5000); // 等待5秒 Mutex := CreateMutex(nil, True, 'Global\MyAppUniqueMutex'); // 使用全局命名空间,以便多用户会话 if Mutex = 0 then RaiseLastOSError; try if GetLastError = ERROR_ALREADY_EXISTS then begin MessageBox(0, '程序已经运行!', '提示', MB_OK or MB_ICONINFORMATION); Exit; end; // 继续运行程序 Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; finally if Mutex <> 0 then ReleaseMutex(Mutex); end; end. ``` 注意:互斥量的名称最好使用“Global\”前缀,以确保在终端服务会话中也是全局的。但如果是普通应用程序,也可以不加。 这样,在重启时,新实例会等待5秒,让旧实例退出。然后新实例创建互斥量,如果此时旧实例已经退出,那么就不会有ERROR_ALREADY_EXISTS错误,新实例可以运行。如果5秒后旧实例仍未退出,则新实例会检测到互斥量已经存在,从而退出。这样虽然重启失败,但旧实例还在运行,所以程序仍然可用。 为了增加可靠性,我们可以增加等待次数,比如等待5秒后,再等待5秒,最多等待3次。但一般情况下,5秒应该足够。 因此,按照上述方案,我们可以实现每天凌晨4点自动重启程序的功能。 注意:如果程序在4点整时处于繁忙状态,可能无法及时响应定时器事件,所以实际重启时间可能会有延迟。如果要求精确,可以缩短定时器检查间隔(比如1秒钟),但这样会增加系统负担。或者,我们可以使用更精确的定时器,但考虑到每天一次,1分钟检查一次已经足够。 最后,记得保存上次重启日期,避免一天内多次重启。 我们使用一个变量LastRestartDate,并在程序启动时从文件或注册表中读取上次重启日期,或者使用一个全局变量(程序退出时保存,启动时读取)。但是,程序可能被强制结束,所以我们可以将日期保存在注册表或ini文件中。 示例:在重启前,将当前日期写入文件或注册表。在程序启动时,读取这个日期,并赋值给LastRestartDate。 这样,即使程序崩溃,下次启动时也能正确读取。 我们可以在窗体创建时从注册表读取LastRestartDate,并在重启时写入新的日期。 例如: 在Form1的OnCreate事件中: ``` // 从注册表读取 Reg := TRegistry.Create; try Reg.RootKey := HKEY_CURRENT_USER; if Reg.OpenKey('Software\MyApp', True) then begin if Reg.ValueExists('LastRestartDate') then LastRestartDate := Reg.ReadDate('LastRestartDate') else LastRestartDate := 0; // 表示没有记录 end; finally Reg.Free; end; ``` 在重启过程中,在调用RestartApplication之前: ``` // 保存重启日期 Reg := TRegistry.Create; try Reg.RootKey := HKEY_CURRENT_USER; if Reg.OpenKey('Software\MyApp', True) then begin Reg.WriteDate('LastRestartDate', Date); end; finally Reg.Free; end; ``` 这样,我们就能确保每天只重启一次。 综上所述,我们结合了定时器、命令行参数、互斥量、注册表操作,实现了在Delphi应用程序中每天凌晨4点自动重启的功能。 注意:在编写代码时,请根据实际需求调整。 相关问题: 1. 如何防止程序在重启时丢失数据? 2. 如果程序在凌晨4点正在执行重要任务,如何避免重启? 3. 除了使用注册表记录重启日期,还有哪些方法可以记录状态? 4. 如何让程序在系统启动时自动运行?</think>在Delphi中实现程序每天凌晨4点自动重启的功能,可以通过以下步骤实现: ### 解决方案 1. **使用定时器检测时间** ```delphi // 在窗体上放置TTimer组件,设置Interval=60000(每分钟检测一次) procedure TMainForm.Timer1Timer(Sender: TObject); var CurrentTime: TDateTime; TargetTime: TDateTime; begin CurrentTime := Now; // 设置目标时间为今天凌晨4点 TargetTime := Trunc(CurrentTime) + EncodeTime(4, 0, 0, 0); // 如果当前时间超过4点但未重启,则设置目标时间为次日4点 if CurrentTime >= TargetTime then TargetTime := TargetTime + 1; // 检测是否到达重启时间(允许±1分钟误差) if Abs(CurrentTime - TargetTime) * 1440 <= 1 then // 1440=24*60 begin RestartApplication; end; end; ``` 2. **重启程序的核心方法** ```delphi procedure RestartApplication; var BatchFile: TextFile; BatchPath: string; ExePath: string; begin ExePath := ParamStr(0); // 获取当前程序路径 // 创建临时批处理文件 BatchPath := ExtractFilePath(ExePath) + 'restart.bat'; AssignFile(BatchFile, BatchPath); Rewrite(BatchFile); Writeln(BatchFile, '@echo off'); Writeln(BatchFile, 'timeout /t 1 /nobreak > nul'); // 等待1秒让当前进程退出 Writeln(BatchFile, 'start "" "' + ExePath + '"'); // 启动新实例 Writeln(BatchFile, 'del /f /q "' + BatchPath + '"'); // 删除自身 CloseFile(BatchFile); // 执行批处理并退出当前程序 WinExec(PAnsiChar(AnsiString(BatchPath)), SW_HIDE); Application.Terminate; end; ``` 3. **防止重复重启** ```delphi // 在窗体创建时读取注册表标记 procedure TMainForm.FormCreate(Sender: TObject); var Reg: TRegistry; begin Reg := TRegistry.Create; try Reg.RootKey := HKEY_CURRENT_USER; if Reg.OpenKey('Software\MyApp', True) then begin // 检查上次重启日期 if Reg.ValueExists('LastRestart') then LastRestartDate := Reg.ReadDateTime('LastRestart'); end; finally Reg.Free; end; end; // 重启前写入注册表标记 procedure TMainForm.RestartApplication; var Reg: TRegistry; begin Reg := TRegistry.Create; try Reg.RootKey := HKEY_CURRENT_USER; if Reg.OpenKey('Software\MyApp', True) then begin Reg.WriteDateTime('LastRestart', Now); end; finally Reg.Free; end; // ...(接前面的重启代码) end; ``` ### 关键点说明 1. **时间检测机制** - 使用`TTimer`每分钟检测一次时间 - 通过`Trunc(Now)`获取当天日期,`EncodeTime`构建4点时间 - 添加±1分钟误差范围避免错过触发 2. **安全重启原理** - 通过批处理文件实现异步重启: ```bat timeout /t 1 // 等待当前进程退出 start "" "程序路径" // 启动新实例 del /f 批处理自身 // 清理痕迹 ``` 3. **防重复触发** - 在注册表记录最后重启时间 - 每天只允许触发一次(通过日期比对) 4. **兼容性考虑** - 使用`WinExec`保持WinXP+兼容 - 批处理路径处理特殊字符(空格等) > **注意**:如果程序需要管理员权限,需在清单文件中设置`requestedExecutionLevel`,并在批处理中使用`runas`命令[^3]。 ### 相关问题 1. 如何让程序在Windows系统启动时自动运行? 2. 怎样实现程序崩溃后自动重启? 3. 除了注册表,还有哪些持久化存储方案可用于记录状态? 4. 如何确保重启过程中不丢失未保存的数据? 5. Linux环境下如何实现类似的定时重启功能? [^1]: 关于自动完成任务的程序实现思路 [^2]: 关机/重启相关的程序交互设计 [^3]: Windows系统命令的定时执行方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值