`
windybell
  • 浏览: 15493 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

昼夜系统-游戏中的时间

    博客分类:
  • JME3
阅读更多
我希望在游戏中能够有昼夜变化,四季变化,这样就意味着游戏中将会有一套自己的时间算法。假设游戏中时间流逝的速度为现实世界的30倍,那么一天就等于在游戏中过了一个月。

随着游戏中时间的变化,会有日出和日落,也会有四季变化。不同的时刻,太阳光照的角度不同,人在地上的影子方向也不一样。比如夏天太阳高度会高一些,冬天则会低一些。



我把游戏世界的位置定位在地球北纬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();
	}

}
  • 大小: 9.8 MB
分享到:
评论

相关推荐

    UE4昼夜交替.pdf

    UE4中的昼夜交替主要依赖于天空光照、时间变化以及动态天气系统等元素的综合运用。具体来说,可以通过调整光照强度、颜色温度以及天空盒的变化来模拟不同时间段的光照效果。此外,还可以结合粒子系统等技术模拟风、...

    制作昼夜变换的真实天空(支持手机)

    在游戏开发或者虚拟环境模拟中,昼夜变换是一个重要的元素,它可以增强沉浸感并提供更为真实的游戏体验。"Time of Day - 制作昼夜变换的真实天空(支持手机)"这个主题主要涉及的是如何创建一个能根据时间变化而改变的...

    网络游戏-一种虚拟游戏场景的图像的提供方法和系统.zip

    此外,实时渲染技术是网络游戏中的关键技术,它允许游戏在短时间内生成高质量的画面。为了保证游戏的流畅性,往往需要在图形质量和性能之间找到平衡,例如使用LOD(Level of Detail)层次细节技术,根据距离和视角...

    unity3d 白天夜晚切换插件 Day_Night_Cycle.unitypackage

    它通常会包含一个易于使用的接口或脚本,允许在游戏逻辑中直接调用相应的函数来切换时间,或者根据游戏内的时间系统自动进行切换。 在实际应用中,可能还会涉及到其他相关技术,如Light Probes(光照探针)用于物体...

    网络游戏-一种基于Cube算法的4D数字影片网络协同创作系统.zip

    通过4D技术,游戏世界可以展现出更丰富的时间变化和动态效果,比如天气系统、昼夜交替、剧情动画等,从而提升玩家的沉浸感。 网络协同创作系统对于游戏开发团队来说是至关重要的工具,它允许开发者在不同地点同时...

    Unity天气系统(Enviro - Sky and Weather)插件

    支持真实的昼夜循环,日月位置和全方位的经纬度,可以选择使用系统时间或根据实时的分钟来更新时间。你有很多选择来调整天空,甚至可以设置怪异的外星人天空!天空盒还包括具有闪烁的真实恒星、可选银河系、具有相位...

    unity白天黑夜时间交替动态天空Time of Day - Dynamic Sky Dom

    3. 时间控制:创建一个脚本,根据`Time.time`计算当前游戏时间,并据此调整天空盒的纹理坐标、太阳位置等参数,实现天空和太阳的动态变化。 4. 动态云层(可选):为了增加真实感,可以添加动态云层效果。这通常...

    OGRE实现天气昼夜变幻效果源码

    总的来说,通过研究和分析这个"OGRE实现天气昼夜变幻效果源码",开发者不仅可以掌握如何在OGRE中实现复杂的天气和时间系统,还可以了解到如何利用插件扩展OGRE的功能,这对于提升3D应用程序的真实感和沉浸感具有重要...

    【Unity 天气系统插件】Enviro 3 - Sky and Weather 高度可定制的云、雾和光照系统

    文件名:Enviro 3 - Sky and Weather v3.1.6b.unitypackage ...动态时间系统:Enviro 3 支持实时的日夜循环,包括昼夜的过渡,太阳和月亮的运动轨迹。 光照调整:随着时间变化,Enviro 3 会自动调整环境光、

    专题资料(2021-2022年)《江山》游戏策划方案.游戏概述doc 35 .doc

    - **游戏时间**:游戏内的时间与现实同步,昼夜更替、季节变化,影响着游戏进程和战略选择。 - **天气**:实时的天气系统,如雨、雪、雾等,影响战斗效果和玩家行动。 **5. 游戏设计** - **技能设计**:角色拥有...

    网络游戏策划案创意书.doc

    - **游戏时间** - 实时变化,对应现实中的昼夜和季节。 - **天气** - 四季交替,影响游戏环境和角色状态。 - **音乐** - 营造氛围,增强游戏沉浸感。 - **天劫** - 渡劫是角色成长的重要环节,考验玩家的毅力和策略...

    UE4天气系统InfinityWeather 4.23-4.27

    此外,InfinityWeather还支持时间流逝的效果,包括日出、日落、昼夜交替,这些都能与天气系统无缝结合,营造出更加生动的场景。 InfinityWeather的另一大亮点是其动态天气过渡。天气不再是一成不变,而是可以平滑地...

    Enviro 3 - Sky and Weather3.0.5(unity插件)

    Enviro 3 - Sky and Weather 3.0.5 是一款专为 Unity 3D 开发的游戏环境模拟插件,它允许开发者创建出逼真的天空、天气效果以及昼夜交替,从而提升游戏或虚拟现实应用的视觉体验。这款插件是Unity开发者的强大工具,...

    impact-day-cycle:ImpactJS 插件,通过转换绘制昼夜循环

    5. 游戏时间系统:理解游戏中的时间系统对游戏体验的影响,如何与其他游戏元素(如敌人行为、玩家能力)相互配合。 通过研究这个插件,开发者不仅可以实现日/夜循环效果,还能进一步提升在ImpactJS平台上的游戏开发...

    Unity强大的动态天空系统Azure Sky Dynamic Skybox v6.0.2

    Unity的Azure Sky Dynamic Skybox是一款高效且功能丰富的动态天空盒系统,专为游戏开发者和虚拟环境创作者设计。这个系统能够模拟真实的天空环境,包括日出、日落、云层变化、大气散射等自然现象,为游戏或应用程序...

    AzureSky Dynamic Skybox 6.0.2

    2. **时间控制**:用户可以自由调整游戏中的时间,包括小时、分钟,甚至季节。这使得天空的颜色、光照强度以及云层的形状和位置都能随着一天中不同时间的变化而动态更新。 3. **光照渲染**:AzureSky考虑了太阳、...

    江山网络游戏简要策划案.doc

    游戏时间的设定可能涉及到昼夜交替、季节变换,甚至可能有实时时间系统,让游戏世界与现实时间同步,提高玩家的代入感。 总的来说,《江山网络游戏简要策划案》是一个全面且深入的项目规划,涵盖了游戏设计的各个...

    Unity游戏Demo

    游戏资源的管理是另一个关键方面,Unity提供了AssetBundle系统,允许开发者将游戏资源打包成独立的文件,按需加载,降低初始加载时间和内存占用。“IslandDemo”的AssetBundle可能包含了场景中的3D模型、纹理、音频...

    基于LibGDX开发的java农场游戏课程设计

    游戏有计时系统,主要用来实现昼夜交替、人物作息、作物生长 昼夜交替:不同时段游戏界面不一样,明暗度会随时间变化 人物作息:到了晚上需要睡觉,睡觉可以恢复体力,否则会晕倒,晕倒会损失体力和金币

Global site tag (gtag.js) - Google Analytics