当前位置:  编程技术>移动开发
本页文章导读:
    ▪ping下令        ping命令 PING命令参数详解  《ping的参数!》  ping [-t] [-a] [-n count] [-l length] [-f] [-i ttl] [-v tos] [-r count] [-s count] [[-j computer-list] | [-k computer-list]  [-w timeout] destination-list  Ping 命令可以用.........
    ▪ (转)游戏发动机剖析        (转)游戏引擎剖析   为了解决“如何在IPHONE上创建一个游戏”这个大问题,我们需要首先解决诸如“如何显示图像”与“如何播放声音”等一系列小问题。这些问题关系到创建部分游戏引擎.........
    ▪ 耳机听到音乐听不见人唱       耳机听见音乐听不见人唱 声波,所有平衡那里调整。 耳机听见音乐听不见人唱 ......

[1]ping下令
    来源: 互联网  发布时间: 2014-02-18
ping命令

PING命令参数详解
  《ping的参数!》
  ping [-t] [-a] [-n count] [-l length] [-f] [-i ttl] [-v tos] [-r count] [-s count] [[-j computer-list] | [-k computer-list]
  [-w timeout] destination-list
  Ping 命令可以用来验证与远程计算机的连接。(该命令只有在安装了TCP/IP协议后才能使用)
  【参数说明】 :
  -t :一直Ping指定的计算机,直到从键盘按下Ctrl+C中断。
  -a :将地址解析为计算机NetBios名。
  -n :发送count指定的ECHO数据包数。,通过这个命令可以自己定义发送的个数,对衡量网络速度很有帮助。能
  够测试发送数据包的返回平均时间,及时间的快慢程度。默认值为 4。
  -l :发送指定数据量的ECHO数据包。默认为 32 字节;最大值是65500byt。
  -f :在数据包中发送“不要分段”标志,数据包就不会被路由上的网关分段。通常你所发送的数据包都会通过路由分
  段再发送给对方,加上此参数以后路由就不会再分段处理。
  -i :将“生存时间”字段设置为TTL指定的值。指定TTL值在对方的系统里停留的时间。同时检查网络运转情况的。
  -v :tos 将“服务类型”字段设置为 tos 指定的值。
  -r :在“记录路由”字段中记录传出和返回数据包的路由。通常情况下,发送的数据包是通过一系列路由才到达目
  标地址的,通过此参数可以设定,想探测经过路由的个数。限定能跟踪到9个路由。
  -s :指定 count 指定的跃点数的时间戳。与参数-r差不多,但此参数不记录数据包返回所经过的路由,最多只记
  录4个。
  -j :利用 computer-list 指定的计算机列表路由数据包。连续计算机可以被中间网关分隔(路由稀疏源) IP 允许的
  最大数量为 9。
  -k :computer-list 利用 computer-list 指定的计算机列表路由数据包。连续计算机不能被中间网关分隔(路由严格
  源)IP 允许的最大数量为 9。
  -w:timeout 指定超时间隔,单位为毫秒。
  destination-list: 指定要 ping 的远程计算机。
  一般情况下,通过ping目标地址,可让对方返回TTL值的大小,通过TTL值可以粗略判断目标主机的系统类型是Windows还是UNIX/Linux,一般情况下Windows系统返回的TTL值在100-130之间,而UNIX/Linux系统返回的TTL值在240-255之间。但TTL的值是可以修改的。故此种方法可作为参考.
  【一般操作方法如下】:
  C:\>ping www.yahoo.com
  Pinging www.yahoo.akadns.net [66.218.71.81] with 32 bytes of data:
  Reply from 66.218.71.81: bytes=32 time=160ms TTL=41
  Reply from 66.218.71.81: bytes=32 time=150ms TTL=41
  Reply from 66.218.71.81: bytes=32 time=160ms TTL=41
  Reply from 66.218.71.81: bytes=32 time=161ms TTL=41
  Ping statistics for 66.218.71.81:
  Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),Approximate
  round trip times in milli-seconds:
  Minimum = 150ms, Maximum = 161ms, Average = 157ms
  --------------------------------------------
  Tracerttracert
  Tracert 该诊断实用程序将包含不同生存时间 (TTL) 值的 Internet 控制消息协议(ICMP)
  回显数据包发送到目标,以决定到达目标采用的路由。要在转发数据包上的 TTL 之前至少递减 1,必需路径上的每个路由器,所以 TTL
  是有效的跃点计数。数据包上的 TTL 到达 0 时,路由器应该将“ICMP 已超时”的消息发送回源系统。Tracert 先发送 TTL
  为 1 的回显数据包,并在随后的每次发送过程将 TTL递增 1,直到目标响应或 TTL
  达到最大值,从而确定路由。路由通过检查中级路由器发送回的“ICMP 已超时”的消息来确定路由。不过,有些路由器悄悄地下传包含过期 TTL
  值的数据包,而 tracert 看不到。
  tracert [-d] [-h maximum_hops] [-j computer-list] [-w timeout]
  target_name
  参数说明:
  /d 指定不将地址解析为计算机名。
  -h maximum_hops 指定搜索目标的最大跃点数。
  -j computer-list 指定沿 computer-list 的稀疏源路由。
  -w timeout 每次应答等待 timeout 指定的微秒数。
  target_name 目标计算机的名称。
  一般操作方法如下:
  C:\>tracert www.yahoo.com
  Tracing route to www.yahoo.akadns.net [66.218.71.81] over a maximum
  of 30 hops:
  1 10 ms <10 ms <10 ms 192.168.0.7
  2 <10 ms 10 ms <10 ms 210.192.97.129
  3 <10 ms 20 ms 10 ms 192.168.200.21
  4 <10 ms 10 ms 10 ms 203.212.0.69
  5 <10 ms 10 ms 10 ms 202.108.252.1
  6 10 ms 10 ms <10 ms 202.106.193.201
  7 10 ms 20 ms 20 ms 202.106.193.169
  8 <10 ms 10 ms 10 ms 202.106.192.226
  9 <10 ms 10 ms 10 ms 202.96.12.45
  10 20 ms 30 ms 20 ms p-6-0-r1-c-shsh-1.cn.net [202.97.34.34]
  11 20 ms 30 ms 30 ms p-3-0-r3-i-shsh-1.cn.net [202.97.33.74]
  12 160 ms 161 ms 160 ms if-7-7.core1.LosAngeles.Teleglobe.net
  [207.45.193.73]
  13 200 ms 201 ms 200 ms if-4-0.core1.Sacramento.Teleglobe.net
  [64.86.83.170]
  14 190 ms 190 ms 190 ms if-2-0.core1.PaloAlto.Teleglobe.net
  [64.86.83.201]
  15 160 ms 160 ms 160 ms ix-5-0.core1.PaloAlto.Teleglobe.net
  [207.45.196.90]
  16 180 ms 180 ms 160 ms ge-1-3-0.msr1.pao.yahoo.com
  [216.115.100.150]
  17 170 ms 210 ms 321 ms vl10.bas1.scd.yahoo.com [66.218.64.134]
  18 170 ms 170 ms 170 ms w2.scd.yahoo.com [66.218.71.81]
  ===============================================
  对于Windows下ping命令相信大家已经再熟悉不过了,但是能把ping的功能发挥到最大的人却并不是很多,当然我也并不是说我可以让ping发挥最大的功能,我也只不过经常用ping这个工具,也总结了一些小经验,现在和大家分享一下。
  现在我就参照ping命令的帮助说明来给大家说说我使用ping时会用到的技巧,ping只有在安装了TCP/IP协议以后才可以使用:
  ping [-t] [-a] [-n count] [-l length] [-f] [-i ttl] [-v tos] [-r count] [-s count] [-j computer-list] | [-k computer-list] [-w timeout] destination-list
  Options:
  -t Ping the specified host until stopped.To see statistics and continue - type Control-Break;To stop - type Control-C.
  不停的ping地方主机,直到你按下Control-C。
  此功能没有什么特别的技巧,不过可以配合其他参数使用,将在下面提到。
  -a Resolve addresses to hostnames.
  解析计算机NetBios名。
  示例:C:\>ping -a 192.168.1.21
  Pinging iceblood.yofor.com [192.168.1.21] with 32 bytes of data:
  Reply from 192.168.1.21: bytes=32 time<10ms TTL=254
  Reply from 192.168.1.21: bytes=32 time<10ms TTL=254
  Reply from 192.168.1.21: bytes=32 time<10ms TTL=254
  Reply from 192.168.1.21: bytes=32 time<10ms TTL=254
  Ping statistics for 192.168.1.21:
  Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),Approximate round trip times in milli-seconds:
  Minimum = 0ms, Maximum = 0ms, Average = 0ms
  从上面就可以知道IP为192.168.1.21的计算机NetBios名为iceblood.yofor.com。
  n count Number of echo requests to send.
  发送count指定的Echo数据包数。
  在默认情况下,一般都只发送四个数据包,通过这个命令可以自己定义发送的个数,对衡量网络速度很有帮助,比如我想测试发送50个数据包的返回的平均时间为多少,最快时间为多少,最慢时间为多少就可以通过以下获知:
  C:\>ping -n 50 202.103.96.68
  Pinging 202.103.96.68 with 32 bytes of data:
  Reply from 202.103.96.68: bytes=32 time=50ms TTL=241
  Reply from 202.103.96.68: bytes=32 time=50ms TTL=241
  Reply from 202.103.96.68: bytes=32 time=50ms TTL=241
  Request timed out.
  ………………
  Reply from 202.103.96.68: bytes=32 time=50ms TTL=241
  Reply from 202.103.96.68: bytes=32 time=50ms TTL=241
  Ping statistics for 202.103.96.68:
  Packets: Sent = 50, Received = 48, Lost = 2 (4% loss),Approximate round trip times in milli-seconds:
  Minimum = 40ms, Maximum = 51ms, Average = 46ms
  从以上我就可以知道在给202.103.96.68发送50个数据包的过程当中,返回了48个,其中有两个由于未知原因丢失,这48个数据包当中返回速度最快为40ms,最慢为51ms,平均速度为46ms。
  l size Send buffer size.
  定义echo数据包大小。
  在默认的情况下windows的ping发送的数据包大小为32byt,我们也可以自己定义它的大小,但有一个大小的限制,就是最大只能发送65500byt,也许有人会问为什么要限制到65500byt,因为Windows系列的系统都有一个安全漏洞(也许还包括其他系统)就是当向对方一次发送的数据包大于或等于65532时,对方就很有可能挡机,所以微软公司为了解决这一安全漏洞于是限制了ping的数据包大小。虽然微软公司已经做了此限制,但这个参数配合其他参数以后危害依然非常强大,比如我们就可以通过配合-t参数来实现一个带有攻击性的命令:(以下介绍带有危险性,仅用于试验,请勿轻易施于别人机器上,否则后果自负)
  C:\>ping -l 65500 -t 192.168.1.21
  Pinging 192.168.1.21 with 65500 bytes of data:
  Reply from 192.168.1.21: bytes=65500 time<10ms TTL=254
  Reply from 192.168.1.21: bytes=65500 time<10ms TTL=254
  ………………
  这样它就会不停的向192.168.1.21计算机发送大小为65500byt的数据包,如果你只有一台计算机也许没有什么效果,但如果有很多计算机那么就可以使对方完全瘫痪,我曾经就做过这样的试验,当我同时使用10台以上计算机ping一台Win2000Pro系统的计算机时,不到5分钟对方的网络就已经完全瘫痪,网络严重堵塞,HTTP和FTP服务完全停止,由此可见威力非同小可。
  -f Set Don<|>t Fragment flag in packet.
  在数据包中发送“不要分段”标志。
  在一般你所发送的数据包都会通过路由分段再发送给对方,加上此参数以后路由就不会再分段处理。
  -i TTL Time To Live.
  指定TTL值在对方的系统里停留的时间。
  此参数同样是帮助你检查网络运转情况的。
  -v TOS Type Of Service.
  将“服务类型”字段设置为 tos 指定的值。
  r count Record route for count hops.
  在“记录路由”字段中记录传出和返回数据包的路由。
  在一般情况下你发送的数据包是通过一个个路由才到达对方的,但到底是经过了哪些路由呢?通过此参数就可以设定你想探测经过的路由的个数,不过限制在了9个,也就是说你只能跟踪到9个路由,如果想探测更多,可以通过其他命令实现,我将在以后的文章中给大家讲解。以下为示例:
  C:\>ping -n 1 -r 9 202.96.105.101 (发送一个数据包,最多记录9个路由)
  Pinging 202.96.105.101 with 32 bytes of data:
  Reply from 202.96.105.101: bytes=32 time=10ms TTL=249
  Route: 202.107.208.187 ->
  202.107.210.214 ->
  61.153.112.70 ->
  61.153.112.89 ->
  202.96.105.149 ->
  202.96.105.97 ->
  202.96.105.101 ->
  202.96.105.150 ->
  61.153.112.90
  Ping statistics for 202.96.105.101:
  Packets: Sent = 1, Received = 1, Lost = 0 (0% loss),
  Approximate round trip times in milli-seconds:
  Minimum = 10ms, Maximum = 10ms, Average = 10ms
  从上面我就可以知道从我的计算机到202.96.105.101一共通过了202.107.208.187 ,202.107.210.214 , 61.153.112.70 , 61.153.112.89 , 202.96.105.149 , 202.96.105.97这几个路由。
  指定 count 指定的跃点数的时间戳。
  此参数和-r差不多,只是这个参数不记录数据包返回所经过的路由,最多也只记录4个。
  -j host-list Loose source route along host-list.
  利用 computer-list 指定的计算机列表路由数据包。连续计算机可以被中间网关分隔(路由稀疏源)IP 允许的最大数量为 9。
  -k host-list Strict source route along host-list.
  利用 computer-list 指定的计算机列表路由数据包。连续计算机不能被中间网关分隔(路由严格源)IP 允许的最大数量为 9。
  -w timeout Timeout in milliseconds to wait for each reply.
  指定超时间隔,单位为毫秒。
  此参数没有什么其他技巧。
  ping命令的其他技巧:在一般情况下还可以通过ping对方让对方返回给你的TTL值大小,粗略的判断目标主机的系统类型是Windows系列还是UNIX/Linux系列,一般情况下Windows系列的系统返回的TTL值在100-130之间,而UNIX/Linux系列的系统返回的TTL值在240-255之间,当然TTL的值在对方的主机里是可以修改的,Windows系列的系统可以通过修改注册表以下键值实现:
  [HKEY_LOCAL_MACHINE\sys tem\CurrentControlSet\Services\Tcpip\Parameters]
  "DefaultTTL"=dword:000000ff
  255---FF
  128---80
  64----40
  32----20
  Ping是个使用频率极高的网络诊断程序,用于确定本地主机是否能与另一台主机交换(发送与接收)数据报。根据返回的信息,你就可以推断TCP/IP参数是否设置得正确以及运行是否正常。需要注意的是:成功地与另一台主机进行一次或两次数据报交换并不表示TCP/IP配置就是正确的,你必须执行大量的本地主机与远程主机的数据报交换,才能确信TCP/IP的正确性。
  ===============================================
  Ping的返回信息有"Request Timed Out"、"Destination Net Unreachable"和"Bad IP address"还有"Source quench received"。
  "Request Timed Out"这个信息表示对方主机可以到达到TIME OUT,这种情况通常是为对方拒绝接收你发给它的数据包造成数据包丢失。大多数的原因可能是对方装有防火墙或已下线。
  "Destination Net Unreachable"这个信息表示对方主机不存在或者没有跟对方建立连接。这里要说明一下"destination host unreachable"和"time out"的区别,如果所经过的路由器的路由表中具有到达目标的路由,而目标因为其它原因不可到达,这时候会出现"time out",如果路由表中连到达目标的路由都没有,那就会出现"destination host unreachable"。
  "Bad IP address" 这个信息表示你可能没有连接到DNS服务器所以无法解析这个IP地址,也可能是IP地址不存在。
  "Source quench received"信息比较特殊,它出现的机率很少。它表示对方或中途的服务器繁忙无法回应。
  ===============================================
  怎样使用Ping这命令来测试网络连通呢?
  连通问题是由许多原因引起的,如本地配置错误、远程主机协议失效等,当然还包括设备等造成的故障。
  首先我们讲一下使用Ping命令的步骤。
  使用Ping检查连通性有六个步骤:
  1. 使用ipconfig /all观察本地网络设置是否正确;
  2. Ping 127.0.0.1,127.0.0.1 回送地址Ping回送地址是为了检查本地的TCP/IP协议有没有设置好;
  3. Ping本机IP地址,这样是为了检查本机的IP地址是否设置有误;
  4. Ping本网网关或本网IP地址,这样的是为了检查硬件设备是否有问题,也可以检查本机与本地网络连接是否正常;(在非局域网中这一步骤可以忽略)
  5.Ping本地DNS地址,这样做是为了检查DNS是否能够将IP。
  6.Ping远程IP地址,这主要是检查本网或本机与外部的连接是否正常。
  ===============================================
  在检查网络连通的过程中可能出现一些错误,这些错误总的来说分为两种最常见。
  1. Request Timed Out
  "request time out"这提示除了在《PING(一)》提到的对方可能装有防火墙或已关机以外,还有就是本机的IP不正确和网关设置错误。
  ①、IP不正确:
  IP不正确主要是IP地址设置错误或IP地址冲突,这可以利用ipconfig /all这命令来检查。在WIN2000下IP冲突的情况很少发生,因为系统会自动检测在网络中是否有相同的IP地址并提醒你是否设置正确。在NT中不但会出现"request time out"这提示而且会出现"Hardware error"这提示信息比较特殊不要给它的提示所迷惑。
  ②、网关设置错误:这个错误可能会在第四个步骤出现。网关设置错误主要是网关地址设置不正确或网关没有帮你转发数据,还有就是可能远程网关失效。这里主要是在你Ping外部网络地址时出错。错误表现为无法Ping外部主机返回信息"Request timeout"。
  2. Destination Host Unreachable
  当你在开始PING网络计算机时如果网络设备出错它返回信息会提示"destination host unreachable"。如果局域网中使用DHCP分配IP时,而碰巧DHCP失效,这时使用 PING命令就会产生此错误。因为在DHCP失效时客户机无法分配到IP系统只有自设IP,它往往会设为不同子网的IP。所以会出现"Destination Host Unreachable"。另外子网掩码设置错误也会出现这错误。
  还有一个比较特殊就是路由返回错误信息,它一般都会在"Destination Host Unreachable"前加上IP地址说明哪个路由不能到达目标主机。这说明你的机器与外部网络连接没有问题,但与某台主机连接存在问题。


PING的各类反馈信息
  Request timed out[1]
  a.对方已关机,或者网络上根本没有这个地址:比如在上图中主机A中PING 192.168.0.7 ,或者主机B关机了,在主机A中PING 192.168.0.5 都会得到超时的信息。
  b.对方与自己不在同一网段内,通过路由也无法找到对方,但有时对方确实是存在的,当然不存在也是返回超时的信息。
  c.对方确实存在,但设置了ICMP数据包过滤(比如防火墙设置)
  怎样知道对方是存在,还是不存在呢,可以用带参数 -a 的Ping命令探测对方,如果能得到对方的NETBIOS名称,则说明对方是存在的,是有防火墙设置,如果得不到,多半是对方不存在或关机,或不在同一网段内。
  d.错误设置IP地址
  正常情况下,一台主机应该有一个网卡,一个IP地址,或多个网卡,多个IP地址(这些地址一定要处于不同的IP子网)。但如果一台电脑的“拨号网络适配器”(相当于一块软网卡)的TCP/IP设置中,设置了一个与网卡IP地址处于同一子网的IP地址,这样,在IP层协议看来,这台主机就有两个不同的接口处于同一网段内。当从这台主机Ping其他的机器时,会存在这样的问题:
  A.主机不知道将数据包发到哪个网络接口,因为有两个网络接口都连接在同一网段。
  B.主机不知道用哪个地址作为数据包的源地址。因此,从这台主机去Ping其他机器,IP层协议会无法处理,超时后,Ping 就会给出一个“超时无应答”的错误信息提示。但从其他主机Ping这台主机时,请求包从特定的网卡来,ICMP只须简单地将目的、源地址互换,并更改一些标志即可,ICMP应答包能顺利发出,其他主机也就能成功Ping通这台机器了。
  Destination host Unreachable
  对方与自己不在同一网段内,而自己又未设置默认的路由,比如上例中A机中不设定默认的路由,运行Ping 192.168.0.1.4就会出现“Destination host Unreachable”。
  网线出了故障
  这里要说明一下“destination host unreachable”和 “time out”的区别,如果所经过的路由器的路由表中具有到达目标的路由,而目标因为其他原因不可到达,这时候会出现“time out”,如果路由表中连到达目标的路由都没有,那就会出现“destination host unreachable”。
  Bad IP address
  这个信息表示您可能没有连接到DNS服务器,所以无法解析这个IP地址,也可能是IP地址不存在。
  Source quench received
  这个信息比较特殊,它出现的机率很少。它表示对方或中途的服务器繁忙无法回应。
  Unknown host——不知名主机
  这种出错信息的意思是,该远程主机的名字不能被域名服务器(DNS)转换成IP地址。故障原因可能是域名服务器有故障,或者其名字不正确,或者网络管理员的系统与远程主机之间的通信线路有故障。
  No answer——无响应
  这种故障说明本地系统有一条通向中心主机的路由,但却接收不到它发给该中心主机的任何信息。故障原因可能是下列之一:中心主机没有工作;本地或中心主机网络配置不正确;本地或中心的路由器没有工作;通信线路有故障;中心主机存在路由选择问题。
  Ping 127.0.0.1:127.0.0.1是本地循环地址
  如果本地址无法Ping通,则表明本地机TCP/IP协议不能正常工作。
  no rout to host:网卡工作不正常。
  transmit failed,error code:10043网卡驱动不正常。
  unknown host name:DNS配置不正确。
[编辑本段]六、相关CMD命令
  1. gpedit.msc-----组策略 
  2. sndrec32-------录音机 
  3. Nslookup-------IP地址侦测器 
  4. explorer-------打开资源管理器 
  5. logoff---------注销命令 
  6. tsshutdn-------60秒倒计时关机命令 
  7. lusrmgr.msc----本机用户和组 
  8. services.msc---本地服务设置 
  9. oobe/msoobe /a----检查XP是否激活 
  10. notepad--------打开记事本 
  11. cleanmgr-------垃圾整理 
  12. net start messenger----开始信使服务 
  13. compmgmt.msc---计算机管理 
  14. net stop messenger-----停止信使服务 
  15. conf-----------启动netmeeting 
  16. dvdplay--------DVD播放器 
  17. charmap--------启动字符映射表 
  18. diskmgmt.msc---磁盘管理实用程序 
  19. calc-----------启动计算器 
  20. dfrg.msc-------磁盘碎片整理程序 
  21. chkdsk.exe-----Chkdsk磁盘检查 
  22. devmgmt.msc--- 设备管理器 
  23. regsvr32 /u *.dll----停止dll文件运行 
  24. drwtsn32------ 系统医生 
  25. rononce -p ----15秒关机 
  26. dxdiag---------检查DirectX信息 
  27. regedt32-------注册表编辑器 
  28. Msconfig.exe---系统配置实用程序 
  29. rsop.msc-------组策略结果集 
  30. mem.exe--------显示内存使用情况 
  31. regedit.exe----注册表 
  32. winchat--------XP自带局域网聊天 
  33. progman--------程序管理器 
  34. winmsd---------系统信息 
  35. perfmon.msc----计算机性能监测程序 
  2. 36. winver---------检查Windows版本 
  37. sfc /scannow-----扫描错误并复原 
  38. taskmgr-----任务管理器(2000/xp/2003 
  39. winver---------检查Windows版本 
  40. wmimgmt.msc----打开windows管理体系结构(WMI) 
  41. wupdmgr--------windows更新程序 
  42. wscript--------windows脚本宿主设置 
  43. write----------写字板 
  44. winmsd---------系统信息 
  45. wiaacmgr-------扫描仪和照相机向导 
  46. winchat--------XP自带局域网聊天 
  47. mem.exe--------显示内存使用情况 
  48. Msconfig.exe---系统配置实用程序 
  49. mplayer2-------简易widnows media player 
  50. mspaint--------画图板 
  51. mstsc----------远程桌面连接 
  52. mplayer2-------媒体播放机 
  53. magnify--------放大镜实用程序 
  54. mmc------------打开控制台 
  55. mobsync--------同步命令 
  56. dxdiag---------检查DirectX信息 
  57. drwtsn32------ 系统医生 
  58. devmgmt.msc--- 设备管理器 
  59. dfrg.msc-------磁盘碎片整理程序 
  60. diskmgmt.msc---磁盘管理实用程序 
  61. dcomcnfg-------打开系统组件服务 
  62. ddeshare-------打开DDE共享设置 
  63. dvdplay--------DVD播放器 
  64. net stop messenger-----停止信使服务 
  65. net start messenger----开始信使服务 
  66. notepad--------打开记事本 
  67. nslookup-------网络管理的工具向导 
  68. ntbackup-------系统备份和还原 
  69. narrator-------屏幕“讲述人” 
  70. ntmsmgr.msc----移动存储管理器 
  71. ntmsoprq.msc---移动存储管理员操作请求 
  72. netstat -an----(TC)命令检查接口 
  73. syncapp--------创建一个公文包 
  74. sysedit--------系统配置编辑器 
  75. sigverif-------文件签名验证程序 
  76. sndrec32-------录音机 
  77. shrpubw--------创建共享文件夹 
  78. secpol.msc-----本地安全策略 
  79. syskey---------系统加密,一旦加密就不能解开,保护windows xp系统的双重密码 
  80. services.msc---本地服务设置 
  81. Sndvol32-------音量控制程序 
  82. sfc.exe--------系统文件检查器 
  83. sfc /scannow---windows文件保护 
  84. tsshutdn-------60秒倒计时关机命令 
  3. 84. tsshutdn-------60秒倒计时关机命令 
  85. tourstart------xp简介(安装完成后出现的漫游xp程序) 
  86. taskmgr--------任务管理器 
  87. eventvwr-------事件查看器 
  88. eudcedit-------造字程序 
  89. explorer-------打开资源管理器 
  90. packager-------对象包装程序 
  91. perfmon.msc----计算机性能监测程序 
  92. progman--------程序管理器 
  93. regedit.exe----注册表 
  94. rsop.msc-------组策略结果集 
  95. regedt32-------注册表编辑器 
  96. rononce -p ----15秒关机 
  97. regsvr32 /u *.dll----停止dll文件运行 
  98. regsvr32 /u zipfldr.dll------取消ZIP支持 
  99. cmd.exe--------CMD命令提示符 
  100. chkdsk.exe-----Chkdsk磁盘检查 
  101. certmgr.msc----证书管理实用程序 
  102. calc-----------启动计算器 
  103. charmap--------启动字符映射表 
  104. cliconfg-------SQL SERVER 客户端网络实用程序 
  105. Clipbrd--------剪贴板查看器 
  106. conf-----------启动netmeeting 
  107. compmgmt.msc---计算机管理 
  108. cleanmgr-------垃圾整理 
  109. ciadv.msc------索引服务程序 
  110. osk------------打开屏幕键盘 
  111. odbcad32-------ODBC数据源管理器 
  112. oobe/msoobe /a----检查XP是否激活 
  113. lusrmgr.msc----本机用户和组 
  114. logoff---------注销命令 
  115. iexpress-------木马捆绑工具,系统自带 
  116. Nslookup-------IP地址侦测器 
  117. fsmgmt.msc-----共享文件夹管理器 
  118. utilman--------辅助工具管理器


    
[2] (转)游戏发动机剖析
    来源: 互联网  发布时间: 2014-02-18
(转)游戏引擎剖析

 

为了解决“如何在IPHONE上创建一个游戏”这个大问题,我们需要首先解决诸如“如何显示图像”与“如何播放声音”等一系列小问题。这些问题关系到创建部分游戏引擎。就像人类的身体一样,游戏引擎的每个部分虽然不同,但是却都不可或缺。因此,首先从游戏引擎剖析开始本章。我们将会讨论一个游戏引擎的所有主要部分,包括应用程序框架、状态机、图像引擎、物理引擎、声音引擎、玩家输入和游戏逻辑。

    写一个好玩的游戏是一项牵扯到很多代码的大任务。非常有必要从一开始就对项目进行良好的,有组织的设计,而不是随着进度的进行而到处杂乱添加代码。就像建造房屋一样,建筑师为整幢房屋勾画蓝图,建筑工人以此来建造。但是,许多对游戏编程不熟悉的编程人员会从根据导读建造出房屋的一部分,并随着学习的进行为其添加房间,这无疑将会导致不好的结果。

 图2-1 游戏引擎的功能结构

 

    图2-1显示了一个适用于大部分游戏的游戏引擎结构。为了理解一个游戏引擎的所有部分和它们是如何工作在一起的,我们可以先为整个游戏做设计,然后再创建我们的应用程序。在以下的几个小节中,我们的讲解内容将会涵盖图2-1的每个部分。

  • 应用程序框架
  • 游戏状态管理器
  • 图像引擎
    应用程序框架

     应用程序框架包含使应用程序工作的必须代码,包括创建一个应用程序实例和初期化其他子系统。当应用程序运行时,会首先创建一个框架类,并接管创建和销毁状态机、图像引擎和声音引擎。如果我们的游戏足够复杂以至于它需要一个物理引擎,框架也会管理它。

    框架必须适应于我们所选择的平台的独特性,包括相应任何的系统事件(如关机与睡眠),以及管理载入与载出资源以使其他的代码只需要集中与游戏。

   主循环

    框架会提供主循环,它是一切互动程序后的驱动力量。在循环中的每一次迭代过程中,程序会检查和处理接受到的事件,运行游戏逻辑中的更新并在必要时将内容描画到屏幕上。(参见图2-2)

  图2-2 主循环序列

 

    主循环如何实现依赖于你使用的系统。对于一个基本的控制台程序,它可能是一个简单的while循环中调用各个函数:

 

view plaincopy to clipboardprint?
  • while( !finished ) {  
  •     handle_events();  
  •     update();  
  •     render();  
  •     sleep(20);  
  • }  
  •  

        注意到这里的sleep函数。它使得代码休眠一小段时间不致于占用全部的CPU。

        有些系统完全不想让用户代码那些写,它们使用了回调系统以强制程序员常规的释放CPU。这样,当应用程序执行后,程序员注册一些函数给系统在每次循环中回调:

     

    view plaincopy to clipboardprint?
  • void main(void) {  
  •     OS_register_event_handler( myEventHandler );  
  •     OS_register_update_function( myUpdate );  
  •     OS_register_render_function( myRender );  
  • }  
  •  

        一旦程序执行后,根据必要情况,那些函数会间隔性的被调用。IPHONE是最接近后面这个例子。你可以在下一章和IPHONE SDK中看到它。

       游戏状态管理器

        一个好的视频游戏不仅有一组动作来维持游戏:它会提供一个主菜单允许玩家来设定选项和开始一个新游戏或者继续上次的游戏;制作群屏将会显示所有辛勤制作这款游戏的人员的名字;而且如果你的游戏没有用户指南,应该一个帮助区域会给用户一些提示告诉他们应该做什么。

        以上任何一种场合都是一种游戏状态,并且代表中一段独立的应用程序代码片段。例如,用户在主菜单调用的函数与导航与用户在制作群屏调用的是完全不同的,所以程序逻辑也是不同的。特别的是,在主菜单,你可能会放一张图片和一些菜单,并且等待用户选择哪个选项,而在制作群屏,你将会把游戏制作人员的名字描绘在屏幕上,并且等待用户输入,将游戏状态从制作群屏改为主菜单。最后,在游戏中状态,将会渲染实际的游戏并等待用户的输入以与游戏逻辑进行交互。

        以上的所有游戏状态都负责相应用户输入、将内容渲染到屏幕、并为该游戏状态提供相对应的应用程序逻辑的任务。你可能注意到了这些任务都来自于之前讨论的主循环中,这是因为它们就是同样的任务。但是,每个状态都会以它们自己的方式来实现这些任务,这也就是为什么要保持他们独立。你不必在主菜单代码中寻找处理游戏中的事件的代码。

       状态机

        状态管理器是一个状态机,这意味着它跟踪着现在的游戏状态。当应用程序执行后,状态机会创建基本的状态信息。它接着创建各种状态需要的信息,并在离开每种状态时销毁暂时存储的信息。

        状态机维护着大量不同对象的状态。一个明显的状态是用户所在屏幕的状态(主菜单、游戏中等)。但是如果你有一个有着人工智能的对象在屏幕上时,状态机也可以用来管理它的“睡眠”、“攻击”、“死亡”状态。

        什么是正确的游戏状态管理器结构?让我们看看一些状态机并决定哪种最适合我们。

        有许多实现状态机的方式,最基本的是一个简单的switch语句: 

    view plaincopy to clipboardprint?
  • class StateManager {  
  •     void main_loop() {  
  •         switch(myState) {  
  •         case STATE_01:  
  •             state01_handle_event();  
  •             state01_update();  
  •             state01_render;  
  •             break;  
  •         case STATE_02:  
  •             state02_handle_event();  
  •             state02_update();  
  •             state02_render;  
  •             break;  
  •         case STATE_03:  
  •             state03_handle_event();  
  •             state03_update();  
  •             state03_render;  
  •             break;  
  •         }  
  •     }  
  • };  
  •  

        改变状态时所有需要做的事情就是改变myState变量的值并返回到循环的开始处。但是,正如你看到的,当我们加入越来越多的状态时,代码块会变得越来越大。而且更糟的是,为了使程序按我们预期的执行,我们需要在程序进入或离开某个状态时执行整个任务块,初始化该状态特定的变量,载入新的资源(比如图片)和释放前一个状态载入的资源。在这个简单的switch语句中,我们需要加入更多的程序块并保证不会漏掉任何一个。

        以上是一些简单重复的劳动,但是我们的状态管理器需要更好的。下面一种更好的实现方式是使用函数指针:

     

    view plaincopy to clipboardprint?
  • class StateManager {  
  •     //the function pointer:  
  •     void (*m_stateHandleEventFPTR) (void);  
  •     void (*m_stateUpdateFPTR)(void);  
  •     void (*m_stateRenderFPTR)(void);  
  •     void main_loop() {  
  •         stateHandleEventFPTR();  
  •         m_stateUpdateFPTR();  
  •         m_stateRenderFPTR();  
  •     }  
  •     void change_state(  void (*newHandleEventFPTR)(void),  
  •                     void (*newUpdateFPTR)(void),  
  •                     void (*newRenderFPTR)(void)  
  •     ) {  
  •         m_stateHandleEventFPTR = newHandleEventFPTR;  
  •         m_stateUpdateFPTR = newUpdateFPTR;  
  •         m_stateRenderFPTR = newRenderFPTR  
  •     }  
  • };  
  •  

        现在,即使我们处理再多状态,主循环也足够小而且简单。但是,这种依然不能帮助我们很好的解决初始化与释放状态。因为每种游戏状态不仅包含代码,还有各自的资源,所以更恰当的做法是将游戏状态作为对象的属性来考虑。因此,接下来,我们将会看看面向对象(OOP)的实现。

        我们首先创建一个表示游戏状态的类:

     

    view plaincopy to clipboardprint?
  • class GameState  
  • {  
  •     GameState();        //constructor  
  •     virtual ~GameState();    //destructor  
  •     virtual void Handle_Event();  
  •     virtual void Update();  
  •     virtual void Render();  
  • };  
  •  

        接着,我们改变我们的状态管理器以使用这个类:

     

    view plaincopy to clipboardprint?
  • class StateManager {  
  •     GameState* m_state;  
  •     void main_loop() {  
  •         m_state->Handle_Event();  
  •         m_state->Update();  
  •         m_state->Render();  
  •     }  
  •     void change_state( GameState* newState ) {  
  •         delete m_state;  
  •         m_state = newState;  
  •     }  
  • };  
  •  

        最后,我们创建一个指定具体游戏状态的类:

     

    view plaincopy to clipboardprint?
  • class State_MainMenu : public GameState  
  • {  
  •     int m_currMenuOption;  
  •     State_MainMenu();  
  •     ~State_MainMenu();  
  •     void Handle_Event();  
  •     void Update();  
  •     void Render();  
  • };  
  •  

        当游戏状态以类来表示时,每个游戏状态都可以存储它特有的变量在该类中。该类也可以它的构造函数中载入任何资源并在析构函数中释放这些资源。

        而且,这个系统保持着我们的代码有很好的组织结构,因为我们需要将游戏状态代码分别放在各个文件中。如果你在查找主菜单代码,你只需要打开State_MainMenu类。而且OOP使得代码更容易重用。

        这个看起来是最适合我们需要的,所以我们决定使用它来作为我们的状态管理器。

       图像引擎

        图像引擎负责视觉输出,包括用户借以交互的图形用户界面(GUI)对象,2D精灵动画或3D模型动画,并渲染的背景与特效。

        虽然渲染2D与3D图片的技术不尽相同,但他们都完成相同的一组图形任务,包括纹理和动画,它们的复杂度是递增的。

        纹理

        对于显示图片,纹理是中心。2D时,一个平面图片是以像素为单位显示在屏幕上,而在3D时,一组三角行(或者被称为网格)在数学魔法作用下产生平面图片并显示在屏幕上。这以后,一切都变得复杂。

        像素、纹理与图片

        当进行屏幕描绘时,基本单位是像素。每个像素都可以被分解为红、绿、蓝色值和我们马上要讨论的Alpha值。

        纹理是一组关于渲染一组像素的数据。它包含每个像素的颜色数据。

        图片是一个更高层的概念,它并非与一组特殊的像素与纹理相关联。当一个人看到一组像素,他的大脑会将它们组合成一幅图片,例如,如果像素以正确的顺序表示,他可能会看到一幅长颈鹿的画像。

        保持以上这些概念独立是非常必要的。纹理可能包含构成长颈鹿图片的像素:它可能包含足够的像素来构成一只长颈鹿的多幅图片,或者仅包含构成一幅长颈鹿图片的像素。纹理本身只是一组像素的集合,它并不固有的知道它包含的是一幅图片。

        透明度

        在任一刻,你的游戏会有几个或者多个物体渲染在屏幕上,其中一些会与另外一个重叠。问题是,如何知道哪个物体的哪个像素应该被渲染出来呢?

        如果在最上层的纹理(在其他纹理之后被描画)是完全不透明的,它的像素将会被显示。但是,对于游戏物体,可能是非矩形图形和部分透明物体,结果会导致两种纹理的结合。

        2D图片中最常用的混合方式是完全透明。假如我们想画一幅考拉(图2-3)在爬在桉树顶上(图2-4)的的图片。考拉的图片以矩形纹理的方式存储在内存中,但是我们不想画出整个矩形,我们只想画出考拉身体的像素。我们必须决定纹理中的每个像素是否应该显示。

        图2-3 考拉纹理

     

     

     

    图2-4 桉树纹理

     

        有些图片系统通过添加一层遮罩来达到目的。想象我们在内存中有一幅与考拉纹理大小一样的另外一份纹理,它只包含白色和黑色的像素。遮罩中每个白色的像素代表考拉应该被描画出来的一个像素,遮罩中的黑色像素则代表不应该被描画的像素。如果当我们将考拉描画到屏幕上时,有这样的一个遮罩层,我们就可以检查考拉对应的像素并仅将需要描画的像素表示出来。如果每个像素有允许有一组范围值而不是二进制黑/白值,那么它还可以支持部分透明(参见图2-5)。

     

     图2-5 考拉遮罩纹理

     

        纹理混合

        为纹理而准备的存储容量大到足以支持每个像素都有一个范围值。典型的是,一个Alpha值占一个字节,即允许0-255之间的值。通过合并两个像素可以表现出有趣的视觉效果。这种效果通常用于部分透明化,例如部分或完全看透物体(图2-6)。

     

     

    图2-6 部分透明的绿色矩形

     

        我们可以为每个像素来设定Alpha以决定它们如何被混合。如果一个像素允许的范围值为0-255,Alpha的范围值也同样应当为0-255。尽管红色值为0表示当描画时不应该使用红色,但Alpha值为0则表示该像素根本不应该被描画。同样,128的红色值表示描画时应该使用最大红色值的一半,128的Alpha值表示当与另外一个像素混合时,应该使用该像素的一半颜色值。

     

        当混合物体时,正确的排列物体顺序是非常重要的。因为每个混合渲染动作都只会渲染源物体与目标物体,首先被描画的物体不会与后描画的物体发生混合。尽管这在2D图片中很容易控制,但是在3D图片中变得非常复杂。

     

        旋转
       

        在2D图片中,大部分的纹理都会被直接渲染到目标上而不需要旋转。这是因为标准硬件不具备旋转功能,所以旋转必须在软件中计算完成。这是一个很慢的过程,而且容易产商低质量的图片。

     

        通常,游戏开发人员会通过预先渲染好物体在各个方向的图片,并当物体某个方向上时,为其在屏幕上描画正确的图片来避免以上问题的发生。

     

        在3D图片中,旋转的计算方式与照明相同,是硬件渲染处理过程中的一部分。

     

        剪贴

        由于某些在后面章节解释的原因,纹理的另外一个重要方面是剪贴。尽管我们目前的例子都是将源纹理直接描画到目标纹理上,但是经常会出现的情况是,需要将部分源纹理描画到目标纹理的有限的一部分上。

     

        例如,如果你的源纹理是在一个文件中含有多幅图片,裁剪允许你仅将希望描画的部分渲染出来。

     

        剪贴同样允许你进行描画限制到目标纹理的一小部分上。它可以帮你通过纹理映射以3D方式渲染物体,将纹理铺盖到三角形组成的任意形状的网格上。例如,一个纹理可以表示衣服或动物的毛皮,而且当3D角色穿着它移动的死后可能产生褶皱。这时候的纹理通常被称作皮肤。

     

        动画

        通过渲染连续的图片,我们可以确保玩家看到一个移动的物体,尽管他所做的仅仅是在同样的像素上,但这些像素在快速的改变颜色。这就是动画的基本概念。2D动画很简单,但3D动画通常牵扯到更多的物体与动作,因此更复杂。

     

        除了讨论动画技巧,这一节还会讨论主要的优化类型可以使得我们的图像引擎有效的和可靠的完成复杂的不可能以原始方式来完成的图形任务。一些主要的优化技巧包括淘汰、纹理排序、使用智能纹理文件、资源管理和细节级别渲染。

     

        2维动画:精灵

        在2D图像中,如果我们要渲染马儿奔驰的完整场景,我们可以先创建出马儿的奔驰各个姿态的图片。这种图片成为一帧。当一帧接一帧的渲染到屏幕上时,马儿动起来了(见图2-7)。这和电影创建动画的方式非常相似,电影也是通过展示连续的帧来达到移动效果。

     

     

     

        图2-7 斯坦福德的马的动作

     

        为了将这些帧保存在一起,我们将它们放在同一个纹理中,称为精灵。通过前面章节我们描述的裁剪方法,将只包含当前帧内容的部分渲染到屏幕上。

     

        你可以将每一帧渲染多次直到渲染该序列的下一帧。这取决于你希望你的动画播放的多快,以及提供了多少帧图片。事实上,通过渲染的帧速和顺序,你可以创造出多种特效。

     

        3维动画:模型

        与2D动画中每次重画时都维护一幅用来渲染的图片--精灵不同,3D动画是通过实际的计算的计算运动的几何效果。正如我们之前描述的,所有的3D物体都由包含一个或多个三角形构成,被称作网格。有多种可以使网格动起来的方法,这些技术与游戏发展与图形硬件有关。这些技术后的基本概念都是:关键帧。

        关键帧与我们之前讨论的2D动画中的帧有些许不同。2维动画的美术人员画出每一帧并保存在纹理中。但是在3D中,只要我们保存了最特殊的几帧,我们就可以通过数学计算得到其他帧。

        最开始的使用网格动画的游戏实际上存储了网格的多个拷贝,每一个拷贝都是都在不同的关键帧方向上。例如,如果我们在3D中渲染马儿,我们应该为上面精灵的每一个关键帧都创建网格。在time(1),第一帧被描画出来,在time(2),第二针被描述出来。

        在主要关键帧之间,使用一种叫做“插值”的技术方法。因为我们知道time(1)的关键帧和time(2)的关键帧有着相同数量的三角形,但是方向稍有区别,我们可以创建当前时间点的临时的,融合了前面两个网格的网格。所以在时间time(1.5),临时网格看起来正好介于time(1)与time(2)之间,而在time(1.8),看起来更偏向于time(2)。

        以上技术效率低下的原因是很明显的。它仅在只有少量的三角形和少量的关键帧时才是可接受的,但是现代图像要求有高解析度与生动细节的动画。幸运的是,有更好的存储关键帧数据的方法。

        这就技术叫做“骨骼动画”(skeletal animation, or bone rigging)。还是以马儿为例,你可能注意到了大多数的三角形都是成组的移动,比如头部组、尾部组和四肢组。如果你将它们都看成是骨头关联的,那么将这些骨头组合起来就形成了骨骼。

        骨骼是由一组可以适用于网格的骨头组成的。当一组骨骼在不同方向连续的表示出来的时候,就形成了动画。每一帧动画都使用的是相同的网格,但是都会有骨头从前一方位移动到下一个方位的细小的动作变化。

        通过仅存储在某一个方位的网格,然后在每一关键帧时都利用它,我们可以创建一个临时的网格并将其渲染到屏幕上。通过在两个关键帧之间插值,我们可以以更小的成本来创建相同的动画。

        动画控制器

        动画控制器对象在抽象低层次的任务非常有用,如选择哪一帧来渲染,渲染多长时间,决定下一帧代替前一帧等。它也起到连接游戏逻辑与图像引擎等动画相关部分的作用。

       在顶层,游戏逻辑只关心将设某些东西,如播放跑动的动画,和设定它的速度为可能应该每秒跑动数个单位距离。控制器对象知道哪个帧序列对应的跑动动画以及这些帧播放的速度,所以,游戏逻辑不必知道这些。

        粒子系统

        另外一个与动画控制器相似的有用对象是粒子系统管理器。当需要描画高度支离破碎的元素,如火焰、云朵粒子、火苗尾巴等时可以使用粒子系统。虽然粒子系统中的每个对象都有有限的细节与动画,它们组合起来却能形成富有娱乐性的视觉效果。

        淘汰

        最好的增加每秒钟描画到屏幕上的次数的方法是在每次迭代中都减少描画在屏幕上的数目的总量。你的场景可能同时拥有成百上千的物体,但是如果你只需要描述其中的一小部分,你仍然可以将屏幕渲染得很快。

        淘汰是从描画路径上移除不必要的物体。你可以在多层次上同时进行淘汰。例如,在一个高层次,一个用户在一间关闭了门的房间里面是看不到隔壁房间的物体的,所以你不必描画出隔壁其他物体。

        在一个低层次,3D图像引擎会经常移除部分你让它们描画的网格。例如,在任意合适的给定时间点,半数的网格几何体在摄影机背面,你从摄像机中看不到这些网格,看到的只是摄影机前方的网格,因此,当网格被渲染时,所有的在摄影机背后的网格都会被忽略。这叫做背面淘汰。

        纹理排序

        每次当一个物体被渲染到屏幕上时,图形硬件都会将纹理源文件载入到内存中。这是被称作上下文交换(context switching)的一部分。

        如果要将三幅图片描画到屏幕上,而其中两幅图片共用同一个纹理资源,有两种办法来处理纹理排序:高效的方法是连续的渲染两幅共享资源的图片,这样只需要以此上下文交换,而低效的方法则需要两次上下文交换。你不应该将第三幅图片放在共享纹理的两幅图片之间描画。

        在渲染处理过程中,通过排列共享纹理的物体可以减少上下文交换的次数,从而提高渲染速度。

        纹理文件

        在一开始就计划好纹理组织结构可以帮助你以最优化方式排列你的纹理。假设你准备在你的游戏中描画几何体,一个主角和一些生物。

        如果前两个关卡是草地,接下来的关卡是沙漠,你可以将所有的树木、草、灌木、岩石以及花儿的图片来放到一起来渲染前两关,并将沙子图片放在另外一个纹理文件中用来渲染第三关。同样的,你可以将玩家虚拟人偶放到一个纹理中。如果所有的生物在所有关卡中都用到了,最优的方式可能是将它们放在一个纹理文件中。但是,如果第一关有吼猴与鼯鼠,而第二关只有森林鼠与苏里南蛤蟆,你可以将第一次前两种动物放在一个纹理中,将后两种放在一个纹理中。

        资源管理

        大部分的视频游戏在一个时间点只会渲染它们所有图片内容的一小部分。将所有纹理同时载入内存是非常低效的。

        幸运的是,游戏设计通常决定了哪些资源在游戏的各个章节是可见的。通过保留必须的纹理为载入状态并卸载不使用的纹理,可以最有效的利用有限的内存资源。

        还是使用前一节的例子,当游戏引擎载入第一关时,资源管理代码必须确保 吼猴与鼯鼠的纹理被载入到内存中。当程序进行到下一关时,资源管理代码会卸载那些纹理,因为它已经知道它们不会在第二关被使用。

        细节层次

        另外一个优化技巧,尤其是对3D图像,叫做细节层次。考虑当一个物体远离摄像机时,它看起来非常小,而且大部分细节都丢失了。你可以描画一个同样大小,却仅拥有简单网格的物体,或者甚至一张平面贴图。

        通过保留不同细节层次的物体的副本在内存中,图像引擎可以根据与摄像机的距离决定使用哪个副本。

       物理引擎

        物理引擎是游戏引擎中负责距离、移动与其它游戏物理相关的部分。不是所有的游戏引擎都需要物理引擎。但是所有的图形游戏都在某种程度上有物理相关代码。

        不相信吗?用“井字游戏”(tic-tac-toe)来举 个例子。确实是一个非常简单的游戏,但是即使这个游戏也有物理部分。当玩家选择一个正方形用来标记的时候,我们必须检查选择的正方形是否有效。如果是,我们就将打上标记并判断玩家是否获胜。这就是物理引擎所完成的两项基本任务的例子:侦测与解决。

        碰撞侦测与碰撞解决

        在你脑海中保持这两方面的独立性非常重要。在游戏代码中,侦测是独立于判定的。不是所有的物体与其它物体会以相同的方式发生碰撞,进而不是被侦测到的所有碰撞都会以相同的方式来解决。

        例如,让我们假想一个游戏:O’Reilly野生冒险。假如玩家的虚拟人偶发现在自己无意间来到了O’Reilly野生保护区,它必须避免奇怪和危险的动物并回到安全的地方。

        这个游戏中会发生以下几种物理交互:

        1.玩家与地图的碰撞

        2.动物与地图的碰撞

        3.玩家与动物的碰撞

        4.玩家与目的地的碰撞

        第一种,玩家与地图的碰撞,非常简单。我们检测玩家的身体区域边界与关卡中的墙。如果玩家将与墙发生碰撞,我们稍微位移一下玩家,以使其不会与墙发生碰撞。

        第二种交互稍微复杂一点。我们使用同样的侦测方法:检测动物的身体区域与关卡中的墙。但是我们的解决方法有一些不同,因为动物不是玩家控制,而是由电脑控制。我们解决情况1时,我们稍微位移一下玩家,以使其不会进入到墙里面。这是用来提醒玩家他正在撞向墙而且必须改变方向。

        如果我们对情况2做同样的解决,AI不会认识到它正撞向墙,而且会继续走向墙里面。因此,我们分两步解决这种情况,首先稍微位移一下动物以使其不会与墙发生碰撞,然后通知AI动物撞上了一面墙。这样,游戏逻辑会控制动物改变移动方向。

        第三种情况,我们首先检查玩家身体区域边界与动物身体区域。一旦我们侦测到了他们将发生碰撞,可能会发生不同的结果。如果这个动物是眼镜猴,他可能会逃跑;如果这个动物是毒蛇或者狮子,它可能会攻击玩家;骆驼可能会忽略玩家;蝗虫可能会被踩扁。

        最后,第四种情况是一种新的情况。目的地与地图、玩家与动物是不同的,因为它没有图形表示。它是一个隐含的触发区域,当玩家踏入它

    时,它会发出一个事件。幸运是,尽管它没有图形表示,它仍然具有物理表示。所以,我们依然可以侦测玩家的身体区域边界与目的地的区域边界。如果我们发现玩家到达了目标,就通知游戏逻辑,使其进入“玩家胜利”的游戏状态。

        二维碰撞侦测

        二维碰撞侦测是一个相对简单的处理过程。大部分都可以总结为如下的一段侦测常规:矩形对矩形、矩形包含点、圆对矩形、圆包含点与圆对圆(有可能也需要检查线段,不过那通常可以被避免)。

        由于这些常规可能被每秒钟使用多次,因此确保它尽可能高效是非常重要的。为了达到这个目的,我们将会进行一系列低成本的测试来证明两个物体碰撞之前是没有碰撞在一起的:

     

    view plaincopy to clipboardprint?
  • bool cd_rectangleToRectangle( Rect r1, Rect r2)  
  • {  
  • //can't be colliding because R1 is too far left of R2  
  • if( r1.x + r1.width < r2.x ) return FALSE;  
  • //can't be colliding because R1 is too far right of R2  
  • if( r1.x > r2.x + r2.width ) return FALSE;  
  • //can't be colliding because R1 is too far below R2  
  • if( r1.y + r1.width < r2.y ) return FALSE;  
  • //can't be colliding because R1 is too far above R2  
  • if( r1.y < r2.y + r2.width ) return FALSE;  
  • //if we get here, the two rects MUST be colliding  
  • return TRUE;  
  • }  
  •  

        尽管这样,当物体碰撞时,还是会有更多的代码被执行。大部分时候,物体都不会相互碰撞,我们针对于此进行了优化,因为它更有效率。

        在我们继续之前,让我们看一个非常重要的概念:游戏对象的图形表示独立于它的物理表示。计算机每秒钟只能提供有限的计算总量,进行实时地物理模拟非常困难。正因为如此,游戏物理代码有一个悠长而骄傲的传统,那就是只要精确到让玩家觉得游戏正确即可。

        比如,在我们上面提到的游戏中,我们玩家的虚拟人偶即将撞上一头犀牛(很明显,要么我们的玩家不太专心,要们就是被一只生气的母老虎追赶)。我们的玩家在跑动过程中,会有很多四肢动作,犀牛也是一样,它伸出它头上的牛角。

        尽管玩家在跑动过程中,图形表示需要出玩家的手和脚,但是在物理表示中,我们真的需要知道四肢的具体坐标吗?我们真的需要去检查玩家的双手、双脚以及头是否与犀牛的头、脚、及牛角发生了碰撞(还有别忘记了尾巴!)?当然不,因为我们的游戏是按照我们的需要简单的用矩形表示玩家和用矩形表示犀牛。

        三位碰撞侦测

        三维碰撞侦测要比二维困难很多。你必须很熟悉三维数学计算,比如线形代数。而且,即使数学要求不高,3D游戏也拥有更复杂的游戏场景。幸运的是,你可以依赖合适的技术来帮助减少计算次数。当然,还是要精确到让玩家觉得游戏正确即可。

        就像我们之前讨论的,一个物体的图像表示不同于它的物理表示。但是,有时我们需要确保他们之间的差别越小越好。想象一下第一人称射击游戏,我们不仅要知道一个玩家知否射中了另外一个玩家,还要知道他是否取得了一个爆头。很明显,一个简单的盒子边界不能满足需求,不过我们也无法提供对每一颗子弹都检查路径,判断其穿过的每一个虚拟人偶的每个三角形的检查。

        我们使用同样的3D侦测,就像我们在2D中使用的侦测一样:在执行精确的与昂贵的测试之前,先执行一系列低成本的否定测试。以子弹为例,我们首先测试子弹有没有划过哪个玩家的边界框。如果没有击中他们,我们退出碰撞检查。对于击中了的边界框,找到起始击中点,并对更个中的所有三角形都做更多详细的测试。这是图形优化中的细节层次的物理版本。如果在最近的玩家身上的检查失败了,我们将会在下个玩家身上做详细检查。用这种方式,我们可以高效并准确的更复杂的场景,比如一颗子弹从一个玩家的双腿间飞过,击中了他身后的另外一位玩家。

        碰撞解决

        当碰撞被侦测到时,就要解决它。首先要考虑的是,什么样的低层次的或高层次的动作必须发生。低层次动作是指物理代码可以解决的,如调整玩家的位置以使其在地面上而不会摔到地面下。高层次的动作是指发送到游戏引擎的游戏逻辑部分的信号。这些信号可以让游戏逻辑知道一个动物何时否跑进了一堵墙里或者一个玩家是否到达了目的地。

        有些碰撞响应需要同时多重高层次的和低层次的响应。例如,在弹钢珠游戏中,一个钢珠打到了防撞杆上,它应该正确的弹离防撞杆(低层次),也会同时向游戏逻辑发送一个信号,使得防撞杆产生动画效果、声音效果以及增加玩家的得分(高层次)。

        当测试低层次的碰撞解决的代码时,要特别注意细节。最影响游戏感觉的重要元素之一就是物理代码。虚拟人偶可以快速的响应玩家的输入吗?赛车游戏能真实地模拟出悬挂系统与变速系统吗?当玩家发生大炮的时候,屏幕会抖动吗?

        根本上,这是由设计人员的工作来让游戏感觉很好,但是这需要程序员的工作来实现它。不要害怕写一些特殊的测试用例以得到正确的结果。

        例如,测试时常用的一项技术是为对象设置“flag”。当一个对象与另外一个对象“ground”接触时,你可以为这个对象设置一个“grounded” flag为true。当grounded flag为true时,可以认为这个物体在休息,不需要对其施加重力影响,或侦测它与地图的碰撞。这不但可以帮助你防止在一大堆静止物体上运行碰撞侦测,也可以避免在某些物理模拟钟,物体在地面附近产生抖动现象。

       声音引擎

        声音是游戏开发中经常忽略的一的环节,但是当你知道声音构成了人类玩视频游戏的1/3的感觉时,你肯定会很困窘。在合适的时机播放正确的声音使得程序员仅作很小的工作就可以为游戏增加分数。

        基本的音效特征包括载入和卸载声音样本、播放声音、停止播放、暂停播放以及循环播放。典型的,所有的声音都可以以同样的音量播放,但是你可能希望玩家调整以使所有的声音都符合他们的喜好。

        声音样本

        在播放音效之前,你需要从文件或者缓存中载入声音样本。IPHONE API支持AAC与MP3格式的高品质的声音,也支持PCM和IMA4的小样品,以及其他一些格式。

       播放声音

        一旦载入了声音样本,API提供了函数以开始和停止播放样本。大多数API还提供了暂停和继续功能,还有一些允许你从特定点开始播放。虽然还有更多高级特性,但是基本上以上就是程序员全部所需要的了。

       多声道声音

        因为声音播放依赖于硬件,你在同一时间可以听到的声音是有限的。每种声音播放时都使用一个声道。在IPHONE中,所有的MP3和AAC样本共用相同的硬件声道,而且只有一个是可用的,多声道支持PCM和IMA4。

        这意味着同一时间,只有一个MP3/AAC格式的样本可以被播放。典型的,但多个PCM/IMA4样本可以同时播放多个样本(同时与MP3/AAC播放)。

        音乐与SFX

        游戏中的大部分声音都可以分为两大类:环境音(典型的,背景音乐)和音效(SFX)。有时,用环境音代替音乐,但他们都共享同样的特点。

        音乐一般是重复播放一个相对长的样本或者引出下一段音乐样本。通常情况下,只有一首音乐在同一时间播放,使得MP3/AAC格式限制变成了一个不成问题的问题。

        声效要短的多,而且需要许多不同的样本,在同一时间重叠播放。PCM/IMA4格式很好的满足了这个需求。

        由于PCM/IMA4也只有有限的声道数,因此如果你打算同时播放很多的SFX样本,其中一些可能不能被播放。所以,为SFX音效设定优先级以确保某些音效一定能播放就显得很重要了。

        例如,让我们讨论之前提到的我们的玩家虚拟人偶走进了一个满是愤怒的犀牛的房间。每头犀牛都会播放愤怒的鼻息声,可能还会接着播放撞人的声音,而玩家虚拟人偶则会发出害怕的哭叫声。我们会给与玩家虚拟人偶声效更高的优先级,以使其不会被犀牛的撞人声效所淹没。

        幸运的是,目前的IPHONE支持至少32声道,所以一般不太可能需要在这个平台上去刻意安排优先级。

        输出设备与干扰

        IPHONE支持内置的对讲机与听筒作为输出设备。建议不要同时使用它们。当玩家将听筒插入到IPHONE中时,音频会自动改为从听筒播放。

        由于IPHONE是移动设备,有可能玩家在路上在的时候,听筒会掉落。一个好的设计选择是当听筒被移除的时候暂停游戏以给玩家足够的时间让他重新接入耳机。此时,你也可以选择同时提供停止播放声音。

        最重要的是,应该从玩家的用户角度来考虑声效。玩你的游戏并不是用户使用IPHONE的唯一标准,所以不要让你的声效的优先级打扰到用户,否则玩家会将其关掉。

       用户输入

        游戏引擎的玩家输入部分集中于来接收自于操作系统的低层次的事件,然后将其转化为高层次的事件,这样,游戏逻辑代码可以在PC游戏中使用它。低层次的事件可以是鼠标和键盘事件。对于控制台游戏,他们可能产生于控制器的手、触发器与按钮。在我们的例子里面,IPHONE会处理触摸与旋转事件。

       触摸事件

        触摸屏接口的设计方式与其他大多数移动手机、控制台与PC平台的接口设计方式有着根本的区别。在用户触摸屏幕与应用程序接收到该事件之间有延迟(尽管IPHONE已经在将缩短延迟方面做的足够好了),但是真正的问题是,不管何时用户做出触摸动作,他的手指都会遮盖住部分屏幕,大大降低了游戏画面的可见性。

        你可以通过提供一个图形按钮给用户来点击(回到了按钮点击系统,代价是屏幕空间),或者提供一个聪明的隐喻来解决这个问题。比如,如果玩家点击了屏幕上的一块空间,你可以让虚拟人偶朝那个方向走。这样可以省去用户的连续点击输入。

        尽管用户接口设计是游戏设计人员的责任,但是编程人员需要告诉设计人员这个平台可以做什么和不能做什么。IPHONE支持以下触摸事件:

    • 触摸开始
    • 触摸移动
    • 触摸结束
    • 触摸取消

        你可能会问什么情况下会触发“触摸取消”事件。当某个事件将你的应用程序挂起在“触摸开始”事件与“触摸结束”事件之间时,程序会收到“触摸取消”事件通知你不会收到其他的事件(如触摸结束)。

        为了处理多点触摸,包含一个UITouch对象list的UIEvent对象被发送到你的应用程序中。如果只有一个指头触摸到屏幕,你只会接收到一个UITouch对象;如果两个指头触摸到屏幕,你会接收到两个UITouch对象等等。而且IPHONE可以追踪正在发生或最近发生的连续的5次轻击事件(一个触摸开始事件后紧跟一个触摸结束事件)。

        不幸的是,通过以上事件来判断用户是单击、双击、扫过或者压缩动作会可能变得比较麻烦。虽然不是很困难,但是在一开始并不是很容易正确处理。看以下例子:

     

    view plaincopy to clipboardprint?
  • Time 0: TouchStart - numTaps(0) numTouches(1) Touches { (40,40) }  
  • Time 100: TouchEnd - numTaps (1) numTouches(1) Touches { (40,40) }  
  • Handle single-tap..  
  •  

        到目前为止,用户触摸了屏幕一次,你的代码可以执行相应的单击处理逻辑。但是稍等!

     

    view plaincopy to clipboardprint?
  • Time 0: TouchStart - numTaps(0) numTouches(1) Touches { (40,40) }  
  • Time 100: TouchEnd - numTaps (1) numTouches(1) Touches { (40,40) }  
  • Handled single-tap.. INCORRECTLY  
  • Time 200: TouchStart - numTaps (1) numTouches(1) Touches { (40,40) }  
  • Time 300: TouchEnd - numTaps (2) numTouches(1) Touches { (40,40) }  
  • FAILED to handle double-tap  
  •  

        用户第二次轻击了屏幕。如果你已经在收到触摸结束的事件时进行了处理,可能错误的处理了用户实际上的双击处理。

        我们应该如何正确处理这种情况呢?解决方法是将第一次触摸结束事件推迟为定时回调。当第一次接收到触摸结束事件时,我们设置一个回调。如果我们在回调之前接收到了第二次触摸结束事件,我们可以判定用户进行了双击,并取消回调,执行双击处理。我们接收到了回调,我们认为用户没有进行双击并应该进行单击处理。

        这里有表达两种情况的例子:

     

    view plaincopy to clipboardprint?
  • Time 0: TouchStart - numTaps(0) numTouches(1) Touches { (40,40) }  
  • Time 100: TouchEnd - numTaps (1) numTouches(1) Touches { (40,40) }  
  • Initiate callback timer  
  • Time 200: TouchStart - numTaps (1) numTouches(1) Touches { (40,40) }  
  • Time 300: TouchEnd - numTaps (2) numTouches(1) Touches { (40,40) }  
  • Handle double-tap, cancel callback  
  •  

        这次,玩家进行了双击而且代码进行了正确的处理。

     

    view plaincopy to clipboardprint?
  • Time 0: TouchStart - numTaps(0) numTouches(1) Touches { (40,40) }  
  • Time 100: TouchEnd - numTaps (1) numTouches(1) Touches { (40,40) }  
  • Initiate callback timer  
  • Time 500: Callback recieved  
  • Handle single-tap  
  •  

        现在,玩家进行了单击而且代码也进行了正确的处理。

        注意,你不必为那些仅期待单击事件的接口加上这些处理。

        侦测诸如扫过的动作会更麻烦一点,但也更容易正确处理。代码中必须为每次触摸保存起始点与终点,并算出用户划的线的方向是向上、向下、向左、还是向右。还要判断他的手划过的是否足够快。

        解决高层次事件

        一旦判定了用户执行的物理动作,你的代码必须能将它们转换为游戏逻辑组件可以使用的形式。具体怎么做需要依赖于你的游戏的上下文,但是这里有几种典型的形式:

    • 如果玩家准备控制虚拟人偶,在玩家和游戏之间通常会有连续的交互。经常需要存储当前用户输入的表现形式。比如,如果输入装置为遥杆,你可能需要在主循环中记录当前点的x轴坐标和y轴坐标,并修正虚拟人偶的动量。玩家和虚拟人偶之间的是紧密地耦合在一起的,所以控制器的物理状态代表着虚拟人偶的高层次的状态模型。当遥杆向前拨动时,虚拟人偶向前移动;当“跳跃”按钮按下时,虚拟人偶跳起。
    • 如果玩家正与游戏地图进行交互,那么需要另外一种间接的方式。比如,玩家必须触摸游戏地图中的一个物体,代码必须将玩家在屏幕上的触摸坐标转化为游戏地图的坐标以判定用户到底触摸到了什么。这可能只是简单的将y轴坐标减去2D摄像机坐标的偏移量,也可能是复杂到3D场景中的摄像机光线碰撞侦测。
    • 最后,用户可能进行一些间接影响到游戏的动作,如暂停游戏、与GUI交互等。这时,一个简单的消息或者函数会被触发,去通知游戏逻辑应该做什么。
        游戏逻辑

        游戏逻辑是游戏引擎中是你的游戏独一无二的部分。游戏逻辑记录着玩家状态、AI状态、判定什么时候达到目的地、并生成所有的游戏规则。给出两个相似的游戏,他们的图像引擎与物理引擎可能只有细微差别,但是它们的游戏逻辑可能会有很大差异。

        游戏逻辑与物理引擎紧密配合,在一些没有物理引擎的小游戏中,游戏逻辑负责处理所有物理相关内容。但是,当游戏引擎中有游戏引擎的时候,需要确保两者的独立。达到此目的的最好方式就是通过物理引擎向游戏逻辑发送高层次的游戏事件。

        高层次事件

        游戏逻辑代码应该尽可能仅处理高层次问题。它不应该处理当用户触摸屏幕时需要以什么顺序将什么描画到屏幕上,或者两个矩形是否相交等问题。它应该处理玩家希望向前移动,什么时候一个新的游戏物体应当被创建/移除以及当两个物体相互碰撞后应该做什么。

        为了维持概念上的距离,处理低层次概念(诸如用户输入与物理引擎等)的代码应当创建高层次的消息并发送给游戏逻辑代码去处理。这不仅能保持代码的独立性与模块化,还会对调试有所帮助。通过查看高层次消息传递的日志,你可以判定是没有正确处理消息(游戏逻辑代码的问题),还是没有在正确的时机传送消息(低层次代码问题)。

        一个非常基本的传递高层次消息的技术是写一个String并传递它。假如玩家按下了上箭头键,它的虚拟人偶必须向上移动。

     

    view plaincopy to clipboardprint?
  • void onPlayerInput( Input inputEvt ) {  
  •     if(inputEvt.type == IE_KEY && inputEvt.value == KEY_UP ) {  
  •         g_myApp->sendGameLogicMessage( "player move forward" );  
  •     }  
  • }  
  •  

        虽然上面的代码对程序员来说通俗易懂,但对于电脑来说却并不高效。它需要更多的内存与处理,远比实际需要的多。我们应该用提示来替代用户输入方法。比起一个字符串,它使用一个"type"和"value"。由于可能的事件都是结构化的和有限的,因此我们可以使用整数和枚举类型来我们消息中的事件信息。

        首先,我们定义一个枚举类型来标识事件类型:

     

    view plaincopy to clipboardprint?
  • enumeration eGameLogicMessage_Types {  
  •     GLMT_PLAYER_INPUT,  
  •     GLMT_PROJECTILE_WEAPON,  
  •     GLMT_GOAL_REACHED,  
  • };  
  •  

        接着我们再创建一个枚举类型来标识事件的值:

     

    view plaincopy to clipboardprint?
  • enumeration eGameLogicMesage_Values {  
  •     GLMV_PLAYER_FORWARD,  
  •     GLMV_PLAYER_BACKWARD,  
  •     GLMV_PLAYER_LEFT,  
  •     GLMV_PLAYER_RIGHT,  
  •     GLMV_ROCKET_FIRED,  
  •     GLMV_ROCKET_HIT,  
  • };  
  •  

        现在我们定义一个结构体来存储我们的消息数据:

     

    view plaincopy to clipboardprint?
  • struct sGameLogicMessage {  
  •     short type;  
  •     short value;  
  • } Message;  
  •  

       现在,我们就可以像上一个例子代码一样,用一个对象来传递我们的消息:

     

    view plaincopy to clipboardprint?
  • void onPlayerInput( Input inputEvt ) {  
  •     if(inputEvt.type == IE_KEY && inputEvt.value == KEY_UP ) {  
  •         Message msg;  
  •         msg.type = GLMT_PLAYER_INPUT;  
  •         msg.value = GLMV_PLAYER_FORWARD;  
  •         g_myApp->sendGameLogicMessage( msg );  
  •     }  
  • }  
  •  

        这看起来作了更多的工作,但它运行起来会更有效率。前一个(坏的)例子用了20个字节来传递消息(20个字符各占一个字节,别忘了终止符)。第二个例子只用了4个字节来传递同样的消息。但是更要的是,当sendGameLogicMessage()处理方法的时候,它只需要分析两个switch语句就可以找到正确的响应,而前一个例子则组要从字符串进行解析,速度很慢。

        人工智能

        游戏逻辑的另外一个职责就是管理AI代理。两类典型的游戏需要用到AI系统:一种是玩家与电脑竞赛;另外一种是在游戏世界中有半自主系统的敌人。在这两种情况下,AI代理为游戏世界中的物体的动作接受输入并提供输出。

        在第一种类型游戏里,AI被称作专家系统。它被期待用来模拟理解游戏规则的人的行为动作,并可以采取具有不同难度的策略来挑战玩家。AI具有与玩家类似的输入与输出,可以近似的模拟玩家的行为。由于人类比现在的AI代理更擅长处理复杂信息,有时为专家系统提供的输入信息要多于给玩家的,以使AI系统看起来更智能。

        例如,在即时战略游戏(RTS)中,战争迷雾用来限制玩家的视野,但AI敌人可以看见地图上所有的单位。尽管这样提高AI对抗更高智慧玩家的能力,但是如果优势变的太大,会让人觉得AI在作,弊。记住,游戏的重要点是让玩家获得乐趣,而不是让AI击败他们。

        在第二种类型的游戏中,可能有许多AI代理。每一个都独立,其不是非常智能。在某些情况下,AI代理会直接面对玩家,而有些可能是中立状态,甚至还有一些是前面两种状态的结合。

        有些代理可能是完全愚笨的,提供特定的、有限的行为而且并不关心游戏世界中发生的事情。在走廊里面来来回回走动的敌人就是一个例子。有些可能是稍微有些愚笨,只有一个输入和一个输出,比如玩家可以打开和关闭的门。还有一些可能非常复杂,甚至懂得将它们的行为组合在一起。为AI代理选择恰当的输入允许你模仿“意识”和增加现实性。

        不论AI代理有多么简单,一般都会它们使用状态机。例如,第一个例子中的完全愚笨的物体必须记录它在朝哪个方向走动;稍微愚笨的物体需要记录它是开的状态还是关的状态。更复杂的物体需要记录“中立”与“进攻性之间的”动作状态,如巡逻、对抗与攻击。

        透明的暂停与继续

        将游戏视作具有主要游戏状态的模拟是非常重要的。不要将现实世界时间与游戏时间混淆。如果玩家决定休息会儿,游戏必须可以暂停。之后,游戏必须可以平滑的继续,就像任何事情都没有发生一样。由于IPHONE是移动设备,保存与继续游戏状态变得尤其重要。

        IPHONE上,在一个时间点只允许一个应用程序运行,用户也希望这些应用程序能够很快载入。同时,他们希望能够继续他们在切换应用程序之前所做的事情。这意味着我们需要具有在设备上保存游戏状态,并尽可能快的继续游戏状态的能力。对于开发游戏,一项任务是要求保持现在的关卡并可以重新载入它使玩家即使在重新启动应用程序后也可以继续游戏。你需要选择保存哪些数据,并以一种小巧的、稳定的格式将其写到磁盘上。这种结构化的数据存储被称为序列化。

       根据游戏类型的不同,这可能比听起来要困难的多。对于一个解谜游戏,你将仅需要记录玩家在哪个关卡、以及现在记分板看起来是什么样的。但是在动作类游戏中,除了记录玩家虚拟人偶之外,你可能还需要记录关卡中的每个物体的位置。在一个特定时间点,这可能变得难以管理,特别是当希望它能够很快完成。对于这种情况,你可以在游戏设计阶段采取一些措施以确保成功。

        首先,你必须决定什么东西是在保存游戏状态时必须保存的。火焰粒子系统中的每根小火苗的位置并不重要,但是在粒子系统的位置在大型游戏中可能很重要。如果它们能从关卡数据中获得,那么游戏中每个敌人的状态可能并不重要。用这种方式进一步考虑,如果你可以简单的让玩家的虚拟人偶从check point开始的话,那玩家虚拟人偶的确切状态与位置也可能不需要保存。

        基于帧的逻辑与基于时间的逻辑

        基于帧的逻辑是指基于单独的帧的改变来更新游戏物体。基于时间的逻辑虽然更复杂但却与实际游戏状态更紧密,是随着时间的流逝而更新游戏物体。

        不熟悉游戏开发的程序员总是犯了将基于帧的逻辑与基于时间的逻辑混合的错误。 它们在定义上的区别是微妙的,不过如果处理不得当,会造成非常明显的BUG。

        比如,让我们以玩家移动为例。新手程序员可能写出这样的代码:

     

    view plaincopy to clipboardprint?
  • void onPlayerInput( Input inputEvent ) {  
  •     if(inputEvt.type == IE_KEY && inputEvt.value == KEY_UP) {  
  •         //apply movement based on the user input  
  •         playerAvatar.y += movementSpeed;  
  •     }  
  • }  
  •  

        每当玩家按下按键,虚拟人偶像前移动一点。这是基于帧的逻辑,因为每次移动的变化都会潜在的伴随着新的帧。事实上,在这个的例子中,每次玩家输入事件都会发生移动。这或多或少有点像主循环的迭代。移动的可视化影响只有在主循环的下次迭代中才会反映,所以任何迭代中间的虚拟人偶移动都会浪费计算。让我们做一下改进:

     

    view plaincopy to clipboardprint?
  • void onPlayerInput( Input inputEvent ) {  
  •     if(inputEvt.type == IE_KEY && inputEvt.value == KEY_UP) {  
  •         //save the input state, but don't apply it  
  •         playerAvatar.joystick = KEY_UP;  
  •     }  
  •     if(inputEvt.type == IE_KEY_RELEASE) {  
  •         playerAvatar.joystick = 0;  
  •     }  
  • }  
  • void Update() {  
  •     //update the player avatar  
  •     if( playerAvatar.joystick == KEY_UP ) {  
  •         playerAvatar.y += movementSpeed;  
  •     }  
  • }  
  •  

        现在我们知道,在键被按下的过程中,每次游戏循环中都只会被赋予一次速度。但是,这仍然是基于帧的逻辑。

        基于帧的逻辑的问题是,帧变化不会总是以相同的时间间隔发生。如果在游戏循环中,渲染或者游戏逻辑会比通常耗费更多的时间,它可能会被推迟到下一次循环中。所以,有时你需要有60帧每秒(fps),有时,你只需要30fps。由于移动是适用于帧的,有时你只会以通常的一半速度来移动。

        你可以用基于时间的逻辑来准确的表达移动。通过记录自从上次帧更新的时间,你可以适用部分移动速度。用这种方式,你可以以每秒为单位来标识移动速度,而不必关心当前帧速率是多少,玩家虚拟人偶的速度是一致的:

     

    view plaincopy to clipboardprint?
  • void Update( long currTime ) {  
  •     long updateDT = currTime - lastUpdateTime;  
  •     //update the player avatar  
  •     if( playerAvatar.joystick == KEY_UP ) {  
  •         //since currTime is in milliseconds, we have to divide by 1000  
  •         // to get the correct speed in seconds.  
  •         playerAvatar.y += (movementSpeed * updateDT)/1000;  
  •     }  
  •     lastUpdateTime = currTime;  
  • }  
  •  

        在这个例子中,移动速度的总量将会是相同的,不管是2fps还是60fps。基于时间的逻辑需要一点额外的代码,但是它可以使程序更精确而不必在乎暂时的延迟。

        当然可以用基于帧的逻辑来开发游戏。重要的是,不要混合它们。比如,如果你的图形代码使用基于时间的逻辑来渲染玩家虚拟人偶的移动动画,但是游戏逻辑代码却使用基于帧的逻辑在游戏世界中来移动它,这样移动的动画将不能玩玩家移动的距离完全同步。

        如果可能的话,请尽量移除基于帧的逻辑。基于时间的逻辑将会对你有更大的帮助。

        游戏逻辑组织结构

        游戏逻辑代码的核心功能就是管理游戏状态的规则与进度。根据你的游戏设计,这可能意味着任何事情。但是,还是有一些基本模式基于制作的游戏的类型。

        游戏逻辑不与任何一个特定的类相关联,它游戏状态对象中表现出来。当主游戏状态被初始化后,它将会为关卡载入与初始化必要的资源。例如猜谜游戏中的一组提示与单词、玩家虚拟人偶的图片数据以及玩家当前所在区域的图片数据。在游戏循环中,游戏逻辑将会接受用户输入,运行物理模拟,并负责处理所有的碰撞结局消息,模拟AI动作,执行游戏规则。最后,当应用程序需要终止主游戏状态,它会释放释放所有的游戏资源,并可能将游戏状态保存到硬盘驱动器上。

        根据游戏的复杂度,你可能会发现很方便进一步分解游戏逻辑。比如,如果你在开发一款冒险游戏,你可能有一个充满环境数据(地面、建筑、河流、树等)、可以移动、与玩家交互的实体(玩家虚拟人偶、敌人、非玩家角色、开关、障碍物等),各种GUI使玩家作出特殊动作和显示重要信息的游戏世界。每种游戏特征都必须有大量的代码。虽然它们合在一起才能组成完整的游戏,但是你还是可以保持它们的工作模块化。

        你可以创建一个Level Manager类来处理游戏关键,包括载入和卸载显示在游戏世界中的物理与图像数据与调用游戏引擎来侦测实体与游戏世界的碰撞。你还可以创建另外一个类或者一些类来处理游戏世界中存在的实体。每个类都载入和卸载渲染那些物体的必要的物理和图片数据,以及包括控制它们的AI。

        最后,你可能创建另外一个单独的类来处理游戏中用户交互,以保持代码与三大概念独立。

        这个体系结构适用于任何类型的游戏。首先评估游戏设计的主要特性,接着以某种方式组合,将相近的功能与数据组合在一起。

        总结

        你应该对创造一个游戏引擎时必须完成的任务有了一个基本的理解。这将会帮助我们在下一节创建这些元素,为我们的游戏做准备。


        
    [3] 耳机听到音乐听不见人唱
        来源: 互联网  发布时间: 2014-02-18
    耳机听见音乐听不见人唱

    声波,所有平衡那里调整。

    耳机听见音乐听不见人唱


        
    最新技术文章:
    ▪Android开发之登录验证实例教程
    ▪Android开发之注册登录方法示例
    ▪Android获取手机SIM卡运营商信息的方法
    ▪Android实现将已发送的短信写入短信数据库的...
    ▪Android发送短信功能代码
    ▪Android根据电话号码获得联系人头像实例代码
    ▪Android中GPS定位的用法实例
    ▪Android实现退出时关闭所有Activity的方法
    ▪Android实现文件的分割和组装
    ▪Android录音应用实例教程
    ▪Android双击返回键退出程序的实现方法
    ▪Android实现侦听电池状态显示、电量及充电动...
    ▪Android获取当前已连接的wifi信号强度的方法
    ▪Android实现动态显示或隐藏密码输入框的内容
    ▪根据USER-AGENT判断手机类型并跳转到相应的app...
    ▪Android Touch事件分发过程详解
    ▪Android中实现为TextView添加多个可点击的文本
    ▪Android程序设计之AIDL实例详解
    ▪Android显式启动与隐式启动Activity的区别介绍
    ▪Android按钮单击事件的四种常用写法总结
    ▪Android消息处理机制Looper和Handler详解
    ▪Android实现Back功能代码片段总结
    ▪Android实用的代码片段 常用代码总结
    ▪Android实现弹出键盘的方法
    ▪Android中通过view方式获取当前Activity的屏幕截...
    ▪Android提高之自定义Menu(TabMenu)实现方法
    ▪Android提高之多方向抽屉实现方法
    ▪Android提高之MediaPlayer播放网络音频的实现方法...
    ▪Android提高之MediaPlayer播放网络视频的实现方法...
    ▪Android提高之手游转电视游戏的模拟操控
     


    站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3