我希望在游戏中能够有昼夜变化,四季变化,这样就意味着游戏中将会有一套自己的时间算法。假设游戏中时间流逝的速度为现实世界的30倍,那么一天就等于在游戏中过了一个月。
随着游戏中时间的变化,会有日出和日落,也会有四季变化。不同的时刻,太阳光照的角度不同,人在地上的影子方向也不一样。比如夏天太阳高度会高一些,冬天则会低一些。
我把游戏世界的位置定位在地球北纬30°(跟武汉一样),那么一年中,春分和秋分两个节气的中午12时,太阳日照高度就是60°。夏至和冬至的太阳高度则要分别加减黄赤夹角(23°26′),为83.5°和36.5°。
我假设游戏中地球公转轨迹是一个非常完美的圆形,那么太阳的高度变化正好是一个正弦函数。地球公转第day天,那么转过的角度就是:
beta = 2 * 3.1415926 * day / 360,
日照高度theta为:
theta = 60°+ 23.5°* sin(beta)。
废话不多说了,上代码。
然后我们写一个实例代码,来看看日出日落的效果。
注意:下例中所有模型,都来源于JME3自带的例子。
随着游戏中时间的变化,会有日出和日落,也会有四季变化。不同的时刻,太阳光照的角度不同,人在地上的影子方向也不一样。比如夏天太阳高度会高一些,冬天则会低一些。
我把游戏世界的位置定位在地球北纬30°(跟武汉一样),那么一年中,春分和秋分两个节气的中午12时,太阳日照高度就是60°。夏至和冬至的太阳高度则要分别加减黄赤夹角(23°26′),为83.5°和36.5°。
我假设游戏中地球公转轨迹是一个非常完美的圆形,那么太阳的高度变化正好是一个正弦函数。地球公转第day天,那么转过的角度就是:
引用
beta = 2 * 3.1415926 * day / 360,
日照高度theta为:
引用
theta = 60°+ 23.5°* sin(beta)。
废话不多说了,上代码。
package org.pstale.utils; import com.jme3.math.Vector3f; /** * 游戏中的时间系统 * @author yanmaoyuan * */ public class GameDate { // 我们假设游戏中时间流动的速度是现实的30倍! public final static int GAME_MINUTE = 2;// 游戏1分钟 = 现实2秒 public final static int GAME_HOUR = 120;// 游戏1小时 = 60游戏分钟 = 现实2分钟 public final static int GAME_DAY = 2880;// 游戏1天 = 24游戏小时 = 现实48分钟 public final static int GAME_MONTH = 86400;// 游戏1月 =30游戏天 = 现实1天 public final static int GAME_YEAR = 1036800;// 游戏1年 = 12游戏月 = 现实12天 // 地球黄赤交角为23°26′ private final static double ECLIPTIC_OBLIQUITY = Math.PI * 23.5f / 180f; // 假设世界的纬度为30°N,春分时中午12点太阳高度为60° private final static double WUHAN_LATITUDE = Math.PI * 60f / 180f; private final static double SEC_DEG = 2 * Math.PI / GAME_DAY;// 地球每秒转动的角度 private final static double SIN_HOUR_DEG = Math.sin(Math.PI / 12);// 日出一小时的高度 private long totalSec;// 游戏从开始到现在总共经过的秒数 private int year_sec; private int month_sec; private int date_sec; private int hour_sec; private int year;// 年份>=0 private int month;// 月份 [0~11] private int date;// 日期 [0~29] private int day;// 一年中的第几天[0~359] private int hour;// 小时[0~23] private int minute;// 分钟[0~59] private double alpha;// 我们假设6点钟日出,α代表时针相对于6点钟的位置。 private double theta;// 我们假设一年每个月正午12点阳光的高度为θ public GameDate() { totalSec = 0l; sunDirection = new Vector3f(); updateTime(); } public GameDate(long lastTime) { totalSec = lastTime; sunDirection = new Vector3f(); updateTime(); } public void update() { totalSec++; updateTime(); } private void updateTime() { //////////// 年月日 year_sec = (int) (totalSec % GAME_YEAR);// 游戏中的一年过了多少秒。 year = (int) (totalSec / GAME_YEAR);// 经过了几年了? day = year_sec / GAME_DAY;// 这是一年的第几天? month_sec = year_sec % GAME_MONTH;// 游戏中一个月过了多少秒 month = year_sec/GAME_MONTH;// 经过了几个月了? date_sec = month_sec % GAME_DAY;// 游戏中的一天过了多少秒 date = month_sec/GAME_DAY;// 经过了几天了? /////////////时分秒 hour_sec = date_sec % GAME_HOUR;// 游戏中的一小时过了多少秒 hour = date_sec / GAME_HOUR;// 今天过了几小时了? minute = (int) (hour_sec / GAME_MINUTE);// 一小时过了几分钟了? // 我们假设6点钟日出,α代表时针相对于6点钟的位置。 alpha = SEC_DEG * (date_sec - GAME_HOUR * 6);// 根据一天的时间,计算时钟的角度 updateDayAndNight(); theta = getTheta();// 根据地球公转的角度,计算日照高度。 updateSunDirection(); } /** * 游戏从开始到现在总共经过的秒数 * @return */ public long currentTimeInSecond() { return totalSec; } /** * 下面来计算太阳高度。太阳每天升起的高度都不一样,随地球公转而变化。 */ public double getTheta() { // 春分是春三月的中节,因此日期要回退45天 double year_angle = Math.PI * 2 * (day - 45) / 360; // 世界的实际日照角度为 this.theta = WUHAN_LATITUDE + ECLIPTIC_OBLIQUITY * Math.sin(year_angle); return theta; } private float lightPower;// 光照强度 private boolean isDay;// 是否是白天 public void updateDayAndNight() { // 计算阳光强度 // 日出和日落时,太阳的亮度会渐变,当高度达到PI/6的时候,天就整个亮了。 // 让日出时间提前1个小时,让日落时间推后1个小时。 lightPower = (float) ((Math.sin(alpha) + SIN_HOUR_DEG) * 2); if (lightPower > 1f) lightPower = 1f; if (lightPower < 0) {// 太阳落下了 lightPower = 0f; if (isDay) isDay = false;// 黑夜 } else { if (!isDay) isDay = true;// 白天 } } public float getLightPower() { return lightPower; } public boolean isDay() { return isDay; } private Vector3f sunDirection;// 光照角度 /** * 下面来计算光照角度 */ public void updateSunDirection() { double x = -Math.cos(alpha); double y = -Math.sin(alpha) * Math.sin(theta); double z = -Math.sin(alpha) * Math.cos(theta); sunDirection.set((float) x, (float) y, (float) z); } public Vector3f getSunDirection() { return sunDirection; } public int getYear() { return year; } public int getMonth() { return month; } public int getDate() { return date; } public int getHour() { return hour; } public int getMinute() { return minute; } }
然后我们写一个实例代码,来看看日出日落的效果。
注意:下例中所有模型,都来源于JME3自带的例子。
package org.pstale.utils; import com.jme3.app.SimpleApplication; import com.jme3.font.BitmapFont; import com.jme3.font.BitmapText; import com.jme3.light.AmbientLight; import com.jme3.light.DirectionalLight; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.math.FastMath; import com.jme3.math.Quaternion; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; import com.jme3.renderer.queue.RenderQueue; import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Cylinder; import com.jme3.shadow.BasicShadowRenderer; import com.jme3.shadow.DirectionalLightShadowRenderer; import com.jme3.texture.Texture.WrapMode; import com.jme3.util.TangentBinormalGenerator; /** * 游戏时间以及昼夜系统测试 * @author yanmaoyuan * */ public class TestGameDate extends SimpleApplication { private GameDate gameDate;// 游戏时间 private DirectionalLight sunLight;// 太阳光 private BitmapText gui;// 用来显示游戏时间 private Geometry sunBox;// 用一个小方块来模拟代表太阳 @Override public void simpleInitApp() { // 初始化游戏时间 gameDate = new GameDate(); // 初始化镜头 cam.setLocation(new Vector3f(27.492603f, 29.138166f, -13.232513f)); cam.setRotation(new Quaternion(0.25168246f, -0.10547892f, 0.02760565f, 0.96164864f)); flyCam.setMoveSpeed(30); // 我们创建一个红色小方块,用来代表太阳,它会根据时间的变化而移动。 Box box = new Box(5,5,5); sunBox = new Geometry("Box", box); Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat.setColor("Color", ColorRGBA.Red); sunBox.setMaterial(mat); sunBox.setShadowMode(ShadowMode.Off); rootNode.attachChild(sunBox); setupGui(); setupLighting(); setupFloor(); setupSignpost(); } /** * 创建gui,用来显示时间 */ public void setupGui() { BitmapFont guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); gui = new BitmapText(guiFont, false); gui.setText("00:00"); guiNode.attachChild(gui); // 把gui放在屏幕顶部居中 float width = (settings.getWidth() - gui.getLineWidth())/2; float height = settings.getHeight(); gui.setLocalTranslation(width, height, 0); } /** * 创建光源 */ public void setupLighting() { // 阳光 sunLight = new DirectionalLight(); sunLight.setColor(ColorRGBA.White.clone()); sunLight.setDirection(gameDate.getSunDirection()); rootNode.addLight(sunLight); // 设置一个很淡的环境光 AmbientLight al = new AmbientLight(); al.setColor(ColorRGBA.White.mult(0.3f)); rootNode.addLight(al); rootNode.setShadowMode(ShadowMode.CastAndReceive); // 阳光产生影子全靠这玩意了! DirectionalLightShadowRenderer dlsr = new DirectionalLightShadowRenderer(assetManager, 512, 4); dlsr.setLight(sunLight); viewPort.addProcessor(dlsr); } /** * 创建一个地板,这样我们才能看见影子。 */ public void setupFloor() { Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m"); mat.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat); mat.getTextureParam("NormalMap").getTextureValue().setWrap(WrapMode.Repeat); mat.setBoolean("UseMaterialColors", true); mat.setColor("Diffuse", ColorRGBA.White.clone()); mat.setColor("Ambient", ColorRGBA.White.clone()); // mat.setColor("Specular", ColorRGBA.White.clone()); // mat.getTextureParam("ParallaxMap").getTextureValue().setWrap(WrapMode.Repeat); mat.setFloat("Shininess", 0); // mat.setBoolean("VertexLighting", true); Box floor = new Box(100, 1f, 100); TangentBinormalGenerator.generate(floor); floor.scaleTextureCoordinates(new Vector2f(5, 5)); Geometry floorGeom = new Geometry("Floor", floor); floorGeom.setMaterial(mat); floorGeom.setShadowMode(ShadowMode.Receive);// 地板只接受影子,不产生影子。 rootNode.attachChild(floorGeom); } /** * 创建一个sign,我们可以看到它在阳光下的影子。 */ public void setupSignpost() { Spatial signpost = assetManager.loadModel("Models/Sign Post/Sign Post.mesh.xml"); Material mat = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m"); signpost.setMaterial(mat); signpost.rotate(0, FastMath.HALF_PI, 0); signpost.setLocalTranslation(0, 3.5f, 0); signpost.setLocalScale(4); signpost.setShadowMode(ShadowMode.CastAndReceive); TangentBinormalGenerator.generate(signpost); rootNode.attachChild(signpost); } @Override public void simpleUpdate(float tpf) { // 更新游戏时间 gameDate.update(); // 更新gui,显示当前时间 gui.setText(String.format("%02d:%02d", gameDate.getHour(), gameDate.getMinute())); // 更新阳光亮度 float power = gameDate.getLightPower(); sunLight.setColor(ColorRGBA.White.clone().mult(power)); // 更新光照角度 sunLight.setDirection(gameDate.getSunDirection()); sunBox.setLocalTranslation(gameDate.getSunDirection().mult(-100f)); } public static void main(String[] args) { TestGameDate app = new TestGameDate(); app.start(); } }
发表评论
-
jME中文网站
2015-12-10 01:58 965jME中文论坛:http://bbs.jmecn.net jM ... -
最近的成果
2015-09-09 18:44 702游戏一共有8个State: Intro、Loading、Use ... -
运行时切换全屏/窗口大小
2015-09-08 16:32 1477JME3没有直接提供改变屏幕大小的接口,我们可以通过setti ... -
JME3骨骼动画研究
2015-08-31 16:47 903最近粗略地看了一下com.jme3.animation包下的源 ... -
JME3资源管理之四:心得和小节
2015-08-28 19:04 1281系列目录: JME3资源管 ... -
JME3资源管理之三:资源加载流程
2015-08-28 17:41 772系列目录: JME3资源管理之一:核心组件介绍 JME3资源管 ... -
JME3资源管理之二:AssetLoader和AssetLocator
2015-08-28 17:39 794系列目录: JME3资源管理之一:核心组件介绍 JME3资源管 ... -
JME3资源管理之一:核心组件介绍
2015-08-28 17:01 1172系列目录: JME3资源管 ... -
3维空间曲线
2015-08-13 19:56 961JME3提供了Curve类,用于 ... -
昼夜变化的天空、飘动的云以及动态的阳光
2015-07-25 14:19 1042神说要有光 要分白天和黑夜 要有日月星辰,昼夜交替出现 要有 ... -
JME3播放背景音乐
2015-07-22 10:52 701JMonkeyEngine3中提供了Audio ...
相关推荐
真实实现随着时间昼夜交换,可设置时间,插件,昼夜交换,真实模拟 package
unity动态昼夜天气系统UniStorm Mobile v2.4.1.2 所支持的Unity版本:5.2.2 及以上版本 UniStorm supports all versions of Unity from version 5.2+ to Unity 2017+.
沙盒类农农场游戏模版项目源码插件 Farming Engine 创建您自己的农业或模拟游戏所需的一切。从 Unity 检查器添加新物品、建筑、植物和动物,用您自己的模型重新设计皮肤,根据您...- 比赛时钟、昼夜循环 -保存/加载系统
行业分类-电子-一种无蓄电池式昼夜光伏水泵系统
支持真实的昼夜循环,日月位置和全方位的经纬度,可以选择使用系统时间或根据实时的分钟来更新时间。你有很多选择来调整天空,甚至可以设置怪异的外星人天空!天空盒还包括具有闪烁的真实恒星、可选银河系、具有相位...
由于每次使用的持续时间,昼夜类型量表评分没有明显差异。 在543名学生中,夜间睡觉时将手机放在自己身体附近的学生占了461名,而晚上将手机放在远离同一房间的床上的学生则更多(p <0.001)。 在夜间睡眠快要...
Time of Day - 制作昼夜变换的真实天空(支持手机);Time of Day - 制作昼夜变换的真实天空(支持手机);Time of Day - 制作昼夜变换的真实天空(支持手机)
苏教版科学五年级上册1.3昼夜交替(4)教案-附知识点梳理.pdf
介绍全球的昼夜长短变化及分布情况,帮助大家理解地球自转、公转的昼夜变化的影响。
行业文档-设计装置-地理教学昼夜长短演示仪专用灯.zip
在目前的敏感性水平下,对于太阳时间和恒星时间情况,可以排除数据中存在任何明显的昼夜变化和昼夜时间结构。 尤其是,由于地球的昼夜运动,基于DAMA暗物质的年度调制结果,预期的昼夜调制幅度低于目前的灵敏度。
昼夜采集助手V2.01 20130601
数字化港口码头远程昼夜视频监控系统.pdf
教科小学科学五年级下册昼夜交替现象PPT课件.pptx
水利航道昼夜监控系统解决方案(激光与红外).doc
行业资料-电子功用-昼夜充电的LED杆式照明装置
行业分类-设备装置-可昼夜连续运转的碟式太阳能斯特林机发电装置.zip
基于Fusion FPGA的昼夜视频采集处理系统研究.pdf
岩溶地表水生系统不同季节的水化学昼夜变化特征及碳汇效应的研究--以重庆丰都雪玉洞流域地下水补给的水池为例 ,胡刘婵,张远瞩,大气CO2浓度的不断上升,正对全球气候变化 产生着十分重要的影响,碳循环问题受到...