前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java国际化/本地化实战

Java国际化/本地化实战

作者头像
JavaEdge
发布于 2020-05-27 02:57:42
发布于 2020-05-27 02:57:42
2.4K00
代码可运行
举报
文章被收录于专栏:JavaEdgeJavaEdge
运行总次数:0
代码可运行

0 前言

全是干货的技术殿堂

文章收录在我的 GitHub 仓库,欢迎Star/fork: Java-Interview-Tutorial https://github.com/Wasabi1234/Java-Interview-Tutorial

开发一个支持多国语言的Web应用程序,要求系统能够根据客户端的系统的语言类型返回对应的界面:英文的操作系统返回英文界面,而中文的操作系统则返回中文界面——这便是典型的i18n国际化问题。

对于有国际化要求的应用系统,我们不能简单地采用硬编码的方式编写用户界面信息、报错信息等内容,而必须为这些需要国际化的信息进行特殊处理。简单来说,就是为每种语言提供一套相应的资源文件,并以规范化命名的方式保存在特定的目录中,由系统自动根据客户端语言选择适合的资源文件。

1 简介

“国际化信息”也称为“本地化信息”,一般需要两个条件才可以确定一个特定类型的本地化信息

  • “语言类型”
  • “国家/地区的类型”

如中文本地化信息既有中国大陆地区的中文,又有中国台湾、中国香港地区的中文,还有新加坡地区的中文。Java通过java.util.Locale类表示一个本地化对象,它允许通过语言参数和国家/地区参数创建一个确定的本地化对象。

语言参数使用ISO标准语言代码表示,这些代码是由ISO-639标准定义的,每一种语言由两个小写字母表示。在许多网站上都可以找到这些代码的完整列表

  • 标准语言代码的信息 http://www.loc.gov/standards/iso639-2/php/English_list.php。

国家/地区参数也由标准的ISO国家/地区代码表示,这些代码是由ISO-3166标准定义的,每个国家/地区由两个大写字母表示

  • 查看ISO-3166的标准代码 http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html
  • 一些语言和国家/地区的标准代码

2 Locale 类

java.util.Locale是表示语言和国家/地区信息的本地化类,是创建国际化应用的基础。下面给出几个创建本地化对象的示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//①带有语言和国家/地区信息的本地化对象  
Locale locale1 = new Locale("zh","CN");   
  
//②只有语言信息的本地化对象  
Locale locale2 = new Locale("zh");   
  
//③等同于Locale("zh","CN")  
Locale locale3 = Locale.CHINA;   
  
//④等同于Locale("zh")  
Locale locale4 = Locale.CHINESE;   
  
//⑤获取本地系统默认的本地化对象  
Locale locale 5= Locale.getDefault();  

用户既可以同时指定语言和国家/地区参数定义一个本地化对象① 也可以仅通过语言参数定义一个泛本地化对象② Locale类中通过静态常量定义了一些常用的本地化对象,③和④处就直接通过引用常量返回本地化对象 用户还可以获取系统默认的本地化对象,如⑤ 在测试时,如果希望改变系统默认的本地化设置,可以在启动JVM时通过命令参数指定: java -Duser.language=en -Duser.region=US MyTest。

本地化工具类

JDK的java.util包中提供了几个支持本地化的格式化操作工具类:NumberFormat、DateFormat、MessageFormat。下面,我们分别通过实例了解它们的用法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Locale locale = new Locale("zh", "CN");  
NumberFormat currFmt = NumberFormat.getCurrencyInstance(locale);  
double amt = 123456.78;  
System.out.println(currFmt.format(amt));   

上面的实例通过NumberFormat按本地化的方式对货币金额进行格式化操作,运行实例,输出以下信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Locale locale = new Locale("en", "US");  
Date date = new Date();  
DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);  
System.out.println(df.format(date));   

通过DateFormat#getDateInstance(int style,Locale locale)方法按本地化的方式对日期进行格式化操作。该方法第一个入参为时间样式,第二个入参为本地化对象。运行以上代码,输出以下信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Jan 8, 2007  

MessageFormat在NumberFormat和DateFormat的基础上提供了强大的占位符字符串的格式化功能,它支持时间、货币、数字以及对象属性的格式化操作。下面的实例演示了一些常见的格式化功能:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 //①信息格式化串  
String pattern1 = "{0},你好!你于{1}在工商银行存入{2} 元。";  
String pattern2 = "At {1,time,short} On{1,date,long},{0} paid {2,number, currency}.";  
  
//②用于动态替换占位符的参数  
Object[] params = {"John", new GregorianCalendar().getTime(),1.0E3};  
  
//③使用默认本地化对象格式化信息  
String msg1 = MessageFormat.format(pattern1,params);   
  
//④使用指定的本地化对象格式化信息  
MessageFormat mf = new MessageFormat(pattern2,Locale.US);   
String msg2 = mf.format(params);  
System.out.println(msg1);  
System.out.println(msg2);  

pattern1是简单形式的格式化信息串,通过{n}占位符指定动态参数的替换位置索引,{0}表示第一个参数,{1}表示第二个参数,以此类推。

pattern2格式化信息串比较复杂一些,除参数位置索引外,还指定了参数的类型和样式。从pattern2中可以看出格式化信息串的语法是很灵活的,一个参数甚至可以出现在两个地方:如 {1,time,short}表示从第二个入参中获取时间部分的值,显示为短样式时间;而{1,date,long}表示从第二个入参中获取日期部分的值,显示为长样式时间。关于MessageFormat更详细的使用方法,请参见JDK的Javadoc。

在②处,定义了用于替换格式化占位符的动态参数,这里,我们使用到了JDK5.0自动装包的语法,否则必须采用封装类表示基本类型的参数值。

在③处,通过MessageFormat的format()方法格式化信息串。它使用了系统默认的本地化对象,由于我们是中文平台,因此默认为Locale.CHINA。而在④处,我们显式指定MessageFormat的本地化对象。

运行上面的代码,输出以下信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
John,你好!你于07-1-8 下午9:58在工商银行存入1,000元。
At 9:58 PM OnJanuary 8, 2007,John paid $1,000.00.

资源文件/属性文件

应用系统中某些信息需要支持国际化功能,则必须为希望支持的不同本地化类型分别提供对应的资源文件,并以规范的方式进行命名。国际化资源文件的命名规范规定资源名称采用以下的方式进行命名:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<资源名>_<语言代码>_<国家/地区代码>.properties

其中,语言代码和国家/地区代码都是可选的。<资源名>.properties命名的国际化资源文件是默认的资源文件,即某个本地化类型在系统中找不到对应的资源文件,就采用这个默认的资源文件。<资源名>_<语言代码>.properties命名的国际化资源文件是某一语言默认的资源文件,即某个本地化类型在系统中找不到精确匹配的资源文件,将采用相应语言默认的资源文件。

举一个例子:假设资源名为resource,则语言为英文,国家为美国,则与其对应的本地化资源文件命名为resource_en_US.properties。信息在资源文件以属性名/值的方式表示:

引用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
greeting.common=How are you!
greeting.morning = Good morning!
greeting.afternoon = Good Afternoon!

对应语言为中文,国家/地区为中国大陆的本地化资源文件则命名为resource_zh_ CN.properties,资源文件内容如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
greeting.common=\u60a8\u597d\uff01
greeting.morning=\u65e9\u4e0a\u597d\uff01
greeting.afternoon=\u4e0b\u5348\u597d\uff01

本地化不同的同一资源文件,虽然属性值各不相同,但属性名却是相同的,这样应用程序就可以通过Locale对象和属性名精确调用到某个具体的属性值了。

上面中文的本地化资源文件内容采用了特殊的编码表示中文字符,这是因为资源文件对文件内容有严格的要求:只能包含ASCII字符。所以必须将非ASCII字符的内容转换为Unicode代码的表示方式。如上面中文的resource_zh_CN.properties资源文件的三个属性值分别是“您好!”、“早上好!”和“下午好!”三个中文字符串对应的Unicode代码串。

如果在应用开发时,直接采用Unicode代码编辑资源文件是很不方便的,所以,通常我们直接使用正常的方式编写资源文件,在测试或部署时再采用工具进行转换。JDK在bin目录下为我们提供了一个完成此项功能的native2ascii工具,它可以将中文字符的资源文件转换为Unicode代码格式的文件,命令格式如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
native2ascii [-reverse] [-encoding 编码] [输入文件 [输出文件]]

resource_zh_CN.properties包含中文字符并且以UTF-8进行编码,假设将该资源文件放到d:\目录下,通过下面的命令就可以将其转换为Unicode代码的形式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
D:\>native2ascii -encoding utf-8 d:\resource_zh_CN.properties
d:\resource_zh_CN_1.properties

由于原资源文件采用UTF-8编码,所以必须显式通过-encoding指定编码格式。

通过native2ascii命令手工转换资源文件,不但在操作上不方便,转换后资源文件中的属性内容由于采用了ASCII编码,阅读起来也不方便。很多IDE开发工具都有属性编辑器的插件,插件会自动将资源文件内容转换为ASCII形式的编码,同时以正常的方式阅读和编辑资源文件的内容,这给开发和维护带来了很大的便利。对于MyEclipse来说,使用MyEclipse Properties Editor编辑资源属性文件;对于Intellij IDEA来说,无须安装任何插件就自然支持资源属性文件的这种编辑方式了。

如果应用程序中拥有大量的本地化资源文件,直接通过传统的File操作资源文件显然太过笨拙。Java为我们提供了用于加载本地化资源文件的方便类java.util.ResourceBoundle。

ResourceBoundle为加载及访问资源文件提供便捷的操作,下面的语句从相对于类路径的目录中加载一个名为resource的本地化资源文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ResourceBundle rb = ResourceBundle.getBundle("com/baobaotao/i18n/resource", locale)  

通过以下的代码即可访问资源文件的属性值:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
rb.getString("greeting.common")  

来看下面的实例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ResourceBundle rb1 = ResourceBundle.getBundle("com/baobaotao/i18n/resource", Locale.US);  
ResourceBundle rb2 = ResourceBundle.getBundle("com/baobaotao/i18n/resource", Locale.CHINA);  
System.out.println("us:"+rb1.getString("greeting.common"));  
System.out.println("cn:"+rb2.getString("greeting.common"));  

rb1加载了对应美国英语本地化的resource_en_US.properties资源文件;而rb2加载了对应中国大陆中文的resource_zh_CN.properties资源文件。运行上面的代码,将输出以下信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
us:How are you!
cn:您好!

加载资源文件时,如果不指定本地化对象,将使用本地系统默认的本地化对象。所以,在中文系统中,ResourceBundle.getBundle(“com/baobaotao/i18n/resource”)语句也将返回和代码清单5-14中rb2相同的本地化资源。

ResourceBundle在加载资源时,如果指定的本地化资源文件不存在,它按以下顺序尝试加载其他的资源:本地系统默认本地化对象对应的资源→默认的资源。上面的例子中,假设我们使用ResourceBundle.getBundle(“com/baobaotao/i18n/resource”,Locale.CANADA)加载资源,由于不存在resource_en_CA.properties资源文件,它将尝试加载resource_zh_CN.properties的资源文件,假设resource_zh_CN.properties资源文件也不存在,它将继续尝试加载resource.properties的资源文件,如果这些资源都不存在,将抛出java.util.MissingResourceException异常。

在资源文件中使用格式化串

在上面的资源文件中,属性值都是一般的字符串,它们不能结合运行时的动态参数构造出灵活的信息,而这种需求是很常见的。要解决这个问题很简单,只须使用带占位符的格式化串作为资源文件的属性值并结合使用MessageFormat就可以满足要求了。

上面的例子中,我们仅向用户提供一般性问候,下面我们对资源文件进行改造,通过格式化串让问候语更具个性化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
greeting.common=How are you!{0},today is {1}
greeting.morning = Good morning!{0},now is {1 time short}
greeting.afternoon = Good Afternoon!{0} now is {1 date long}

将该资源文件保存在fmt_resource_en_US.properties中,按照同样的方式编写对应的中文本地化资源文件fmt_resource_zh_CN.properties。

下面,我们联合使用ResourceBoundle和MessageFormat得到美国英文的本地化问候语:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 //①加载本地化资源  
ResourceBundle rb1 =   
             ResourceBundle.getBundle("com/baobaotao/i18n/fmt_ resource",Locale.US);   
ResourceBundle rb2 =   
              ResourceBundle.getBundle("com/baobaotao/i18n/fmt_ resource",Locale.CHINA);  
Object[] params = {"John", new GregorianCalendar().getTime()};  
  
  
String str1 = new MessageFormat(rb1.getString("greeting.common"),Locale. US).format(params); ②  
String str2 =new MessageFormat(rb2.getString("greeting.morning"),Locale. CHINA).format(params);  
String str3 =new MessageFormat(rb2.getString("greeting.afternoon"),Locale. CHINA).format(params);  
System.out.println(str1);  
System.out.println(str2);  
System.out.println(str3);  

运行以上的代码,将输出以下信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
How are you!John,today is 1/9/07 4:11 PM
早上好!John,现在是下午4:11
下午好!John,现在是200719

MessageSource

Spring定义了访问国际化信息的MessageSource接口,并提供了几个易用的实现类。首先来了解一下该接口的几个重要方法: String getMessage(String code, Object[] args, String defaultMessage, Locale locale) code表示国际化资源中的属性名;args用于传递格式化串占位符所用的运行期参数;当在资源找不到对应属性名时,返回defaultMessage参数所指定的默认信息;locale表示本地化对象; String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException 与上面的方法类似,只不过在找不到资源中对应的属性名时,直接抛出NoSuchMessageException异常; String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException MessageSourceResolvable 将属性名、参数数组以及默认信息封装起来,它的功能和第一个接口方法相同。

MessageSource分别被HierarchicalMessageSource和ApplicationContext接口扩展,这里我们主要看一下HierarchicalMessageSource接口的几个实现类,如图5-7所示:

HierarchicalMessageSource接口添加了两个方法,建立父子层级的MessageSource结构,类似于前面我们所介绍的HierarchicalBeanFactory。该接口的setParentMessageSource (MessageSource parent)方法用于设置父MessageSource,而getParentMessageSource()方法用于返回父MessageSource。

HierarchicalMessageSource接口最重要的两个实现类是ResourceBundleMessageSource和ReloadableResourceBundleMessageSource。它们基于Java的ResourceBundle基础类实现,允许仅通过资源名加载国际化资源。ReloadableResourceBundleMessageSource提供了定时刷新功能,允许在不重启系统的情况下,更新资源的信息。StaticMessageSource主要用于程序测试,它允许通过编程的方式提供国际化信息。而DelegatingMessageSource是为方便操作父MessageSource而提供的代理类。

ResourceBundleMessageSource

该实现类允许用户通过beanName指定一个资源名(包括类路径的全限定资源名),或通过beanNames指定一组资源名。 通过JDK的基础类完成了本地化的操作,下面我们使用ResourceBundleMessageSource来完成相同的任务。读者可以比较两者的使用差别,并体会Spring所提供的国际化处理功能所带给我们的好处: 通过ResourceBundleMessageSource配置资源

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<bean id="myResource"  
class="org.springframework.context.support.ResourceBundleMessageSource">  
    <!--①通过基名指定资源,相对于类根路径-->  
    <property name="basenames">    
       <list>  
          <value>com/baobaotao/i18n/fmt_resource</value>  
       </list>  
    </property>  
  </bean>   

启动Spring容器,并通过MessageSource访问配置的国际化资源,如代码清单 5 19所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String[] configs = {"com/baobaotao/i18n/beans.xml"};  
ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);  
  
//①获取MessageSource的Bean  
MessageSource ms = (MessageSource)ctx.getBean("myResource");   
Object[] params = {"John", new GregorianCalendar(). getTime()};  
  
//②获取格式化的国际化信息  
String str1 = ms.getMessage("greeting.common",params,Locale.US);  
String str2 = ms.getMessage("greeting.morning",params,Locale.CHINA);  
String str3 = ms.getMessage("greeting.afternoon",params,Locale.CHINA);  
System.out.println(str1);  
System.out.println(str2);  
System.out.println(str3);  

比较代码清单5-19中的代码,我们发现最主要的区别在于我们无须再分别加载不同语言、不同国家/地区的本地化资源文件,仅仅通过资源名就可以加载整套的国际化资源文件。此外,我们无须显式使用MessageFormat操作国际化信息,仅通过MessageSource# getMessage()方法就可以完成操作了。这段代码的运行结果与代码清单5 17的运行结果完全一样。

ReloadableResourceBundleMessageSource

前面,我们提到该实现类比之于ResourceBundleMessageSource的唯一区别在于它可以定时刷新资源文件,以便在应用程序不重启的情况下感知资源文件的变化。很多生产系统都需要长时间持续运行,系统重启会给运行带来很大的负面影响。这时,通过该实现类就可以解决国际化信息更新的问题。请看下面的配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<bean id="myResource"   
lass="org.springframework.context.support. ReloadableResourceBundleMessageSource">  
   <property name="basenames">  
      <list>  
        <value>com/baobaotao/i18n/fmt_resource</value>  
      </list>  
   </property>  
   <!--① 刷新资源文件的周期,以秒为单位-->  
   <property name="cacheSeconds" value="5"/>   
 </bean>  

在上面的配置中,我们通过cacheSeconds属性让ReloadableResourceBundleMessageSource每5秒钟刷新一次资源文件(在真实的应用中,刷新周期不能太短,否则频繁的刷新将带来性能上的负面影响,一般不建议小于30分钟)。cacheSeconds默认值为-1表示永不刷新,此时,该实现类的功能就蜕化为ResourceBundleMessageSource的功能。

我们编写一个测试类对上面配置的ReloadableResourceBundleMessageSource进行测试:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String[] configs = {"com/baobaotao/i18n/beans.xml"};  
ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);  
  
MessageSource ms = (MessageSource)ctx.getBean("myResource");  
Object[] params = {"John", new GregorianCalendar().getTime()};  
  
for (int i = 0; i < 2; i++) {  
    String str1 = ms.getMessage("greeting.common",params,Locale.US);      
    System.out.println(str1);  
    Thread.currentThread().sleep(20000); //①模拟程序应用,在此期间,我们更改资源文件   
}  

在①处,我们让程序睡眠20秒钟,在这期间,我们将fmt_resource_zh_CN.properties资源文件的greeting.common键值调整为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
---How are you!{0},today is {1}---

我们将看到两次输出的格式化信息分别对应更改前后的内容,也即本地化资源文件的调整被自动生效了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
How are you!John,today is 1/9/07 4:55 PM
---How are you!John,today is 1/9/07 4:55 PM---

容器级的国际化信息资源

在如图5-7所示的MessageSource类图结构中,我们发现ApplicationContext实现了MessageSource的接口。也就是说ApplicationContext的实现类本身也是一个MessageSource对象。

将ApplicationContext和MessageSource整合起来,乍一看挺让人费解的,Spring这样设计的意图究竟是什么呢?原来Spring认为:在一般情况下,国际化信息资源应该是容器级。我们一般不会将MessageSource作为一个Bean注入到其他的Bean中,相反MessageSource作为容器的基础设施向容器中所有的Bean开放。只要我们考察一下国际化信息的实际消费场所就更能理解Spring这一设计的用意了。国际化信息一般在系统输出信息时使用,如Spring MVC的页面标签,控制器Controller等,不同的模块都可能通过这些组件访问国际化信息,因此Spring就将国际化消息作为容器的公共基础设施对所有组件开放。

既然一般情况下我们不会直接通过引用MessageSource Bean使用国际信息,那如何声明容器级的国际化信息呢?我们其实在5.1.1节讲解Spring容器的内部工作机制时已经埋下了伏笔:在介绍容器启动过程时,我们通过代码清单5-1对Spring容器启动时的步骤进行剖析,④处的initMessageSource()方法所执行的工作就是初始化容器中的国际化信息资源:它根据反射机制从BeanDefinitionRegistry中找出名称为“messageSource”且类型为org.springframework.context.MessageSource的Bean,将这个Bean定义的信息资源加载为容器级的国际化信息资源。请看下面的配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!--①注册资源Bean,其Bean名称只能为messageSource -->  
<bean id="messageSource"   
      class="org.springframework.context.support.ResourceBundleMessageSource">  
  <property name="basenames">  
     <list>  
       <value>com/baobaotao/i18n/fmt_resource</value>  
     </list>  
  </property>  
</bean>  

下面,我们通过ApplicationContext直接访问国际化信息,如代码清单5 23所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String[] configs = {"com/baobaotao/i18n/beans.xml"};  
ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);  
//①直接通过容器访问国际化信息  
Object[] params = {"John", new GregorianCalendar().getTime()};  
   
String str1 = ctx.getMessage("greeting.common",params,Locale.US);  
String str2 = ctx.getMessage("greeting.morning",params,Locale.CHINA);     
System.out.println(str1);  
System.out.println(str2);  

运行以上代码,输出以下信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
How are you!John,today is 1/9/07 5:24 PM
早上好!John,现在是下午5:24

假设MessageSource Bean名字没有命名为“messageSource”,以上代码将抛出NoSuchMessageException异常。

参考

  • Spring 4.x企业应用开发实战
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/03/02 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Unity精华☀️三、四元数(Quaternion)解决万向锁
星河造梦坊官方
2024/08/15
2340
Unity精华☀️三、四元数(Quaternion)解决万向锁
Unity基础(17)-四元数与欧拉角与矩阵
Quaternion中存放了x,y,z,w四个数据成员,可以用下标来进行访问,对应的下标分别是0,1,2,3 其实最简单来说:四元数就是表示一个3D物体的旋转,它是一种全新数学数字,甚至不是复数。 四元数其实就是表示旋转。
孙寅
2020/06/02
5.3K0
Unity基础(17)-四元数与欧拉角与矩阵
Unity3D中的Quaternion(四元数)
四元数,这是一个图形学的概念,一般没怎么见过,图形学中比较常见的角位移的表示方法有“矩阵”、“欧拉角”、“四元数”这三种。可以说各有各的优点和不足,不同的场合用不同的方法。其中四元数的优点有:平滑插值、快速连接、角位移求逆、可以与矩阵形式快速转换、仅用四个数表示。不过,它也有一些缺点:比欧拉角多一个数表示、可能不合法(如:坏的输入数据或者浮点数累计都可能使四元数不合法,不过可以通过四元数标准化来解决这个问题)、晦涩难懂。
bering
2019/12/02
6.3K0
【Unity3D 灵巧小知识点】 ☀️ | 求解 两个向量的夹角度数
Unity 小科普 老规矩,先介绍一下 Unity 的科普小知识: Unity是 实时3D互动内容创作和运营平台 。 包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者,借助 Unity 将创意变成现实。 Unity 平台提供一整套完善的软件解决方案,可用于创作、运营和变现任何实时互动的2D和3D内容,支持平台包括手机、平板电脑、PC、游戏主机、增强现实和虚拟现实设备。 也可以简单把 Unity 理解为一个游戏引擎,可以用来专业制作游戏! ---- Unity小知识点学习 求解 两个向量的夹角度
呆呆敲代码的小Y
2021/09/29
1.9K0
【Unity3D 灵巧小知识点】 ☀️ | 求解 两个向量的夹角度数
unity3d:向量计算,摄像机与目标位置
立羽
2023/08/24
2340
Eigen中四元数、欧拉角、旋转矩阵、旋转向量之间的转换
旋转向量 1,初始化旋转向量:旋转角为alpha,旋转轴为(x,y,z) Eigen::AngleAxisd rotation_vector(alpha,Vector3d(x,y,z)) 2,旋转向量转旋转矩阵 Eigen::Matrix3d rotation_matrix; rotation_matrix=rotation_vector.matrix(); Eigen::Matrix3d rotation_matrix; rotation_matrix=rotation_vector.toRotati
点云PCL博主
2022/01/27
3.4K0
鼠标控制物体旋转、移动、缩放(Unity3D)
一、前言 Unity3D对于鼠标操作物体的旋转、移动、缩放的功能点使用的比较多。 今天就分享如何使用Unity实现鼠标对于物体的旋转、移动、缩放。 效果图: 二、知识点 Input.GetMouseButton(0) 获取鼠标输入,参数为一个int值 为0的时候获取的是左键 Input.GetMouseButton(1) 为1的时候获取的是右键 Input.GetMouseButton(2) 为2的时候获取的是中键(就是那个滑轮) Input.GetMouseButton 鼠标按压 I
恬静的小魔龙
2022/08/07
4.3K1
鼠标控制物体旋转、移动、缩放(Unity3D)
Unity 以一定角速度转向动态目标的旋转方式对比
②难以判断何时应该停止旋转,且角速度过大时很容易造成在到达目标向量附近来回鬼畜旋转
汐夜koshio
2020/07/15
2.5K0
【Unity3D 灵巧小知识点】☀️ | Unity 屏幕坐标 和 世界坐标 之间相互转换
Unity 小科普 老规矩,先介绍一下 Unity 的科普小知识: Unity是 实时3D互动内容创作和运营平台 。 包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者,借助 Unity 将创意变成现实。 Unity 平台提供一整套完善的软件解决方案,可用于创作、运营和变现任何实时互动的2D和3D内容,支持平台包括手机、平板电脑、PC、游戏主机、增强现实和虚拟现实设备。 也可以简单把 Unity 理解为一个游戏引擎,可以用来专业制作游戏! ---- Unity小知识点学习 Unity 屏幕坐标
呆呆敲代码的小Y
2021/09/14
7390
【Unity3D 灵巧小知识点】☀️ | Unity 移动物体到指定位置的几种方法【精选快捷使用】
上述代码是将gameObject的本地坐标transform.localPosition,变成一个我们赋予的新坐标new Vector3(0, 0, 100),按照我们设置的一个float的速度进行移动
呆呆敲代码的小Y
2021/10/22
5.6K0
Unity第三人称视角解决方案
镜头跟随 在实现第三人称时,镜头问题困扰了我一整天,参考了官方的脚本 SmoothFollow,虽然能实现镜头跟在人物身后,但是发现几个问题。 脚本实现太繁琐,有几个属性目前根本就用不到。 人物旋转时不能控制摄像机跟着旋转,也就是说,不能让镜头一直跟在人物身后。 脚本代码如下: public class SmoothFollow : MonoBehaviour { // The target we are following [SerializeField] private Transfo
xferris
2018/06/01
2.7K0
【100个 Unity小知识点】 | Unity中的 eulerAngles、localEulerAngles细节剖析
Unity中的 rotation 、 localRotation 和 eulerAngles、localEulerAngles都是用来表示旋转的一个API
呆呆敲代码的小Y
2021/12/13
2.5K0
【100个 Unity小知识点】 | Unity中的 eulerAngles、localEulerAngles细节剖析
Unity2D游戏开发-常用的计算方法
若正好在区间内则直接返回原值,如果比最大值大则返回最大值,若比最小值小则返回最小值。
码客说
2023/08/08
3690
three.js 欧拉角和四元数
这篇郭先生就来说说欧拉角和四元数,欧拉角和四元数的优缺点是老生常谈的话题了,使用条件我就不多说了,我只说一下使用方法。
郭先生的博客
2020/08/31
1.9K0
three.js 欧拉角和四元数
Unity基础(8)-Transform组件与类
01-Unity下的Transform组件 Transform组件 02-Transform类包含的属性 // 获得当前Transform的子Transform的个数 childCount
孙寅
2020/06/02
1.5K0
Unity基础(8)-Transform组件与类
unity3d:向量计算:得到围绕物体一圈点位置
立羽
2023/08/24
2020
unity3d:向量计算:得到围绕物体一圈点位置
[学习笔记]三维数学(4)-物体的旋转
欧拉角 什么是欧拉角 用三个数去存储物体在x、y、z轴的旋转角度。 补充: 为了避免万向节死锁,y和z轴取值范围都是0~360°,x轴是-90°~90°。 x和z轴是旋转是相对于自身坐标轴的,y轴旋转永远是相对于世界坐标轴的。 优点 好理解,使用方便 只用三个数表示,占用空间少,在表示方位的数据结构中是占用最少的 缺点 万向节死锁 四元数 什么是四元数 Quaternion在3D图形学中表示旋转,由一个三维向量(X/Y/Z)和一个标量(W)组成。 旋转轴为V,
六月丶
2022/12/26
5240
unity3d:保持V字型队形,按路径点移动
思路: 1.分为领导者,追随者,追随点。 2.先创建领导者。根据剩下人数的多少,再在左右创建追随者和追随点,成v字排列。追随点是领导者的子物体 3.领导者按照路径点移动 4.追随者追的追随点移动,如果距离过远,要提高速度
立羽
2023/08/24
2580
unity3d:保持V字型队形,按路径点移动
unity 方向向量_二面角距离公式
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/178797.html原文链接:https://javaforall.cn
全栈程序员站长
2022/09/27
3750
Ubuntu安装Eigen进行OpenCV矩阵变换
目录 一:安装Eigen (1)安装 方式一、直接命令安装 方式二、源码安装: (2)移动文件 二:使用Eigen——旋转矩阵转换欧拉角 三:其他用法示例 简单记录下~~ Eigen是一个基于C++模板的开源库,支持线性代数,矩阵和矢量运算,数值分析及其相关的算法。 官网:Eigen 一:安装Eigen (1)安装 方式一、直接命令安装 sudo apt-get install libeigen3-dev 方式二、源码安装: https://gitlab.com/libeigen/eigen/-
小锋学长生活大爆炸
2022/05/09
1.3K0
Ubuntu安装Eigen进行OpenCV矩阵变换
推荐阅读
相关推荐
Unity精华☀️三、四元数(Quaternion)解决万向锁
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档