PlayStation® Vita上的“Hello World”:将代码部署到Vita






4.92/5 (22投票s)
将代码部署到PlayStation® Vita的过程简介。

引言
2012 年 11 月 19 日,索尼宣布其 PlayStation Mobile 开发者计划已结束 Beta 测试并正式启动。该计划允许开发者制作针对一系列 PlayStation 认证设备的应用程序和游戏。这些设备包括 PlayStation Vita 和许多其他基于 Android 的设备。
我的目的是只编写一个“Hello World”应用程序并让它在 PS Vita 上运行。虽然我会讨论本文中的一些代码,但本文不侧重于代码本身。相反,我的目标是记录我为在 Vita 上运行 Hello World 程序所需做的一切,包括满足索尼的部署设备要求。我在此任务中遇到了一些障碍。但从文档的角度来看,这是一件好事,因为这可以帮助其他开发者更轻松地避免我遇到的障碍。
要求
- Windows 7(不支持 Windows 8)
- PlayStation Mobile SDK (PSM SDK)
- (可选) PlayStation 认证设备
- 熟悉 C#
- 熟悉像素和顶点着色器
- 居住在 SDK 支持的国家/地区之一
要开始,您需要一台运行 Windows 7 的 Windows PC。在撰写本文时,Windows 8 尚不支持。尽管 Windows 8 是我正在使用的操作系统。拥有实际的 PlayStation 硬件是可选的。如果您正在阅读本文,您可能打算以 Vita 为目标。但也有许多 Android 设备可以作为目标。使用 PM SDK 编写的程序是用 C# 编写的。PM SDK 使用 Mono;.Net 框架的一个版本,可在多个非 Microsoft 平台上运行。
与大多数应用商店一样,PSM 在初始阶段仅支持有限数量的国家/地区。这些国家/地区包括美国、日本、加拿大、英国、法国、德国、意大利、西班牙和澳大利亚。
注册
在开始之前,您需要拥有一个 Sony Entertainment Network 账户(又名 PlayStation 账户)。我跳过了如何注册账户的步骤,因为我是在 5 年前注册的,不记得过程了(即使记得,也不知道它是否仍然相同)。如果您已经在使用 PlayStation 系列硬件的设备,您很可能已经有一个账户。使用您的账户,您需要注册 PlayStation 开发者计划。这是我遇到的第一个障碍。在我决定注册的那天,PlayStation 网络因维护而关闭。维护窗口的结束时间每几个小时推迟一次,网络直到周六当地时间凌晨 2 点左右才真正恢复联机。网络恢复后,我才得以注册。
导航到 http://playstation.com/psm 并选择“PlayStation® Mobile Developer Program”选项。“注册”按钮将在下一页。

输入后续页面中要求的注册信息并接受协议,您就可以下载 SDK 了。
下载和安装
在您的 PlayStation Vita 上搜索 PSM Mobile Developer Assistant。任何时候部署到设备时,您都需要确保此程序正在运行。我没有在其他地方明确提及这一点。因此,请记住在与设备交互时使其运行。

要下载 SDK,请访问 http://playstation.com/psm 并点击 PlayStation® Mobile Developer 链接。然后点击右上角的“登录”。登录后,您就可以下载 SDK 了。“创建”链接

向下滚动一点,您会看到同意 SDK 许可并下载软件的按钮。软件安装过程与其他安装大同小异。您需要多次单击“下一步”按钮。
如果您运行的是 Windows 8,这可能是您遇到障碍的地方。当我尝试安装时,一切都很顺利,除了安装部署到 PlayStation Vita 所需的驱动程序。

返回的唯一错误消息是驱动程序安装失败。我尝试单独安装驱动程序。驱动程序文件将复制到 C:\Program Files (x86)\SCE\PSM\tools\vita\driver
。但是驱动程序安装仍然失败。在互联网上搜索后,我找到了一些关于 Windows 8 Release Candidate 的 解决方案。这些解决方案在我使用的 RTM 版本上不起作用。直到我查看 c:\windows\dpinst.log 中的安装日志,我才明白是怎么回事。
INFO: Install option set: legacy mode on. INFO: Found driver package: 'C:\Program Files (x86)\SCE\PSM\tools\vita\driver\vita_usbser.inf'. INFO: Preinstalling 'c:\program files (x86)\sce\psm\tools\vita\driver\vita_usbser.inf' ... INFO: ENTER: DriverPackagePreinstallW INFO: RETURN: DriverPackagePreinstallW (0xE0000247)
搜索错误号(0xE0000247
)得到“缺少或不正确的受信任根证书颁发机构/证书。”驱动程序安装失败是因为它未签名。要解决此问题,必须关闭 Windows 8 中的驱动程序签名强制执行。这个选项有点深埋。
在 Windows 8 中,打开设置应用程序并选择“常规”。在最后一个设置“高级”下,有一个标有“立即重新启动”的按钮。这将重新启动您的计算机,并将您带到一个可以禁用驱动程序签名强制执行的界面。

禁用驱动程序签名后,我再次尝试运行驱动程序安装。这次我收到了一条关于驱动程序未签名的警告。

我选择了“仍然安装此驱动程序软件”。之后,软件安装就完成了。在进行安装时,您可以通过在安装任何 PlayStation 开发软件之前关闭驱动程序签名来节省一些时间。
已安装内容的概览
UI Composer

UI Composer 允许您构建基于小部件的用户界面。乍一看,它的工作方式与其他拖放式 UI 生成器类似。这个工具对于当前的任务不是必需的,所以我不会再讨论它。
PlayStation® Mobile Studio (PSM)

这将是您将使用的主要工具。PSM 将用于管理您的项目和编辑源代码。您将使用此工具进行调试和部署您的软件。
Publishing Utility

您需要 publishing utility 来管理您程序的证书、代码签名,并且顾名思义,用于发布到商店。
创建新项目
启动 PSM Studio。在欢迎页面上,选择“开始新解决方案”选项。在“新建解决方案”窗口中,“C#”节点下可以找到“PlayStation Mobile Application”项目模板。请注意,虽然也有 VB 节点,但您不能使用 VB 来制作 PSM 应用程序。

给您的应用程序命名,然后选择“确定”。最终结果是一个渲染黑色屏幕的程序。如果您按 [F5],程序将在模拟器中运行。为了确保程序确实在做一些事情,让我们改变它填充屏幕的背景颜色。程序的源代码在左侧的解决方案资源管理器中。向下导航到 AppMain.cs 文件并双击它。

查找源代码末尾的 Render 方法。对 SetColor()
的调用被赋予了全零值。这些参数是红色、绿色、蓝色和 Alpha 的值。将它们更改为 1.0,屏幕将渲染为白色(或者在 0.0 到 1.0 之间的值之间尝试,直到找到您喜欢的颜色。再次按 F5 运行程序,您应该会看到屏幕以不同的颜色渲染。

在继续之前,让我们看一下实际代码。Initialize()
方法用于创建游戏循环开始前程序所需的各种对象。在这里,它只用于创建 graphicsContext
对象,该对象将执行我们所有的渲染。Main 函数运行一个循环,该循环将不断调用 Update()
函数来执行您的游戏逻辑和读取用户输入,然后调用 Render() 来更新屏幕。SystemEvents.CheckEvents()
的调用必须每帧发生一次。它用于检查系统事件,并在应用程序从最小化状态恢复时触发对 OnRestored()
的调用。
public static void Main (string[] args) { Initialize (); while (true) { SystemEvents.CheckEvents (); Update (); Render (); } }
显示图像
PSM 支持多种图形格式,但唯一支持透明度的格式是 PNG。我们将把 PNG 文件添加到项目中并在屏幕上显示它。右键单击您的项目,然后选择“添加”->“新建文件夹”在项目中创建一个新文件夹。将文件夹命名为“resources”。右键单击这个新文件夹,然后选择“添加”->“添加文件”。选择要显示的图像。我不是世界上最好的艺术家,所以为了本文的缘故,我抓取了一张旧游戏的精灵图,并从中选取了一个精灵。

为了确保图像以正确的设置导入,请右键单击它并选择“属性”。当构建窗口打开时,确保构建操作设置为“Content”
要在屏幕上显示图像,我们将创建一个带有纹理(即我们的图像)的矩形。然后我们将矩形绘制到屏幕上。要定义矩形,我们需要定义其四个角的点。点本身并不构成一个矩形。我们需要告诉渲染引擎这些点是如何连接的。渲染引擎实际上绘制三角形。因此,我们将绘制两个三角形,通过以连点成线的方式告诉渲染引擎哪些角属于哪个三角形。我们需要定义一些数据来完成这个任务:矩形的角(顶点)、每个角如何映射到我们的图像(纹理坐标)、连接点的顺序(索引列表)以及应该应用于每个角的颜色(蒙皮)。如果您不想使用蒙皮,则应选择白色。
屏幕坐标
如果您熟悉 OpenGL,这对您来说将是旧闻。如果您习惯使用 Microsoft 的 API 之一,这与您可能习惯的不同。在大多数 Microsoft 图形 API 中,屏幕左上角的像素是 (0,0)。在 OpenGL 中,左上角是 (-1, 1)。坐标 (0,0) 是屏幕中心,(1, -1) 是右下角。如果您正在处理精灵,您可能更愿意以像素为单位而不是 OpenGL 坐标系进行工作。因此,我们需要一个转换矩阵来将精灵的坐标系转换为屏幕坐标。
让我们声明所有这些项目将需要的变量。
static Texture2D mySprite;
static Matrix4 screenMatrix;
static Vector3 CurrentPosition = new Vector3();
static VertexBuffer myVertexBuffer;
我们还需要矩形角坐标、纹理坐标的数据
#region vertex data
static float[] vertices = new float[]
{
0f, 0f, 0f,
0f, 1f, 0f,
1f, 0f, 0f,
1f, 1f, 0f,
};
static float[] textureCoordinates = new float[]
{
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f,
};
static float[] colors = new float[]
{
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
static ushort[] indexList =
{
0, 1, 2, 3,
};
#endregion
将上述内容添加到程序后,请尝试再次运行它。您应该看不到任何变化。它只会显示空白背景。但请尝试编译并运行,以便及早发现编译错误。
我们需要将顶点数据放入一个可以被渲染引擎使用的对象中,初始化我们的坐标转换矩阵,并加载图像。所有这些都将在 Initialize()
方法中完成。我为屏幕转换矩阵定义了一个方法,因为我们将需要多次使用它。这是我们需要用于屏幕转换矩阵的方法。
static void UpdatePositionMatrix(Vector3 p)
{
ImageRect rectScreen = graphics.Screen.Rectangle;
float xOffset = (p.X )/(rectScreen.Width/1.0f);
float yOffset = (p.Y )/(rectScreen.Height/1.0f);
screenMatrix = new Matrix4(
myPicture.Width*2.0f/rectScreen.Width, 0.0f, 0.0f, 0.0f,
0.0f, myPicture.Height*(-2.0f)/ rectScreen.Height, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
-1.0f + xOffset, 1.0f - yOffset, 0.0f, 1.0f
);
}
Initialize 方法应更新为如下所示:
public static void Initialize ()
{
// Set up the graphics system
graphics = new GraphicsContext ();
UISystem.Initialize(graphics);
myPicture = new Texture2D("/Application/resources/Thermostat.jpg",false);
myShader = new ShaderProgram("/Application/shaders/Simple.cgx");
mySound = new Sound("/Application/resources/LOZ_Get_Item.wav");
mySoundPlayer = mySound.CreatePlayer();
myShader.SetUniformBinding(0, "WorldViewProj");
myVertexBuffer = new VertexBuffer(4, indexList.Length,VertexFormat.Float3, VertexFormat.Float2, VertexFormat.Float4);
myVertexBuffer.SetVertices(0, vertices);
myVertexBuffer.SetVertices(1, textureCoordinates);
myVertexBuffer.SetVertices(2, colors);
myVertexBuffer.SetIndices(indexList);
graphics.SetVertexBuffer(0, myVertexBuffer);
UpdatePositionMatrix(new Vector3());
return;
}
最后,我们需要更新渲染代码。
public static void Render ()
{
// Clear the screen
graphics.SetClearColor (0.0f, 0.0f, 1.0f, 1.0f);
graphics.Clear ();
graphics.SetShaderProgram(myShader);
graphics.SetTexture(0, mySprite);
myShader.SetUniformValue(0, ref screenMatrix);
graphics.SetVertexBuffer(0,myVertexBuffer);
graphics.DrawArrays(DrawMode.TriangleStrip, 0, indexList.Length);
// Present the screen
graphics.SwapBuffers ();
}
尝试运行程序。您会注意到有问题;程序渲染了一个黑色的矩形而不是我们的精灵。问题出在顶点着色器上。我们正在使用默认的顶点着色器,这不是我们需要的。让我们看看着色器。打开 Shader 文件夹并双击 Simple.vcg。
void main( float4 in a_Position : POSITION,
float4 out v_Position : POSITION,
uniform float4x4 WorldViewProj
)
{
v_Position = mul( a_Position, WorldViewProj );
}
回头看顶点缓冲区声明的地方。它包含了一些我们需要的信息。
myVertexBuffer = new VertexBuffer(4, indexList.Length,VertexFormat.Float3, VertexFormat.Float2, VertexFormat.Float4);
myVertexBuffer.SetVertices(0, vertices);
myVertexBuffer.SetVertices(1, textureCoordinates);
myVertexBuffer.SetVertices(2, colors);
根据这个,我们的顶点缓冲区有 4 个顶点。每个顶点将有一个 Float3(由三个浮点数组成的结构)、一个 Float2 和一个 Float4。Float4 是顶点的位置。Float2 是纹理坐标,Float4 是一个 4 分量颜色。这与默认顶点着色器期望的不匹配。默认顶点着色器期望没有颜色信息的顶点信息。因此,我们需要更改为着色器声明的参数。默认着色器只输出位置信息。但我希望它输出更新后的位置、颜色(蒙皮)和纹理坐标信息。这些信息将在像素着色器中进一步处理。
顶点着色器应如下所示:
void main( float3 in in_Position : POSITION,
float2 in in_TextCoord : TEXCOORD0,
float4 in in_Color: COLOR0,
float4 out v_Position : POSITION,
float2 out v_TextCoord: TEXCOORD0,
float4 out v_Color: COLOR0,
uniform float4x4 WorldViewProj
)
{
v_Position = mul( float4(in_Position, 1.0f), WorldViewProj );
v_TextCoord = in_TextCoord;
v_Color = in_Color;
}
默认形式的像素着色器将无法正确解释此信息。让我们看看默认着色器。
void main( float4 out Color : COLOR,
uniform float4 MaterialColor )
{
Color = MaterialColor;
}
默认形式下,它只期望颜色信息,并计划 unmodified 显示该像素的颜色。让我们更新它以处理
void main(
float4 in v_Position : POSITION,
float2 in v_TextCoord: TEXCOORD0,
float4 in v_Color: COLOR0,
float4 out finalColor: COLOR,
uniform sampler2D s_Texture : TEXUNIT0 )
{
float4 x = tex2D(s_Texture, v_TextCoord);
x.r = x.r*v_Color.r;
x.g = x.g*v_Color.g;
x.b = x.b*v_Color.b;
x.a = x.a*v_Color.a;
if(x.a==0.0f)
discard;
finalColor=x;
}
更新着色器后,如果您现在运行程序,您将在左上角看到精灵渲染。

用户输入和移动精灵
PSM SDK 支持两种用户输入模式。一种是触摸屏输入,另一种是控制按钮输入。运行 PSM 游戏的设备包括一些没有控制摇杆或按钮可用于输入的手机。PlayStation® Vita 同时支持触摸屏和控件。对于这个 hello world 程序,我将只使用游戏手柄。我希望精灵能够响应按下方向键或左模拟摇杆的输入而移动。
通过一次调用 GamePad.GetData(0)
就可以获取所有按钮和模拟摇杆的状态。此方法返回一个 GamePadData
对象。在本地 PC 上调试时,请记住模拟器不模拟任何模拟输入。您必须拥有 PS Vita 才能测试任何涉及模拟输入的代码。现在,让我们进行修改以响应用户按下 8 向方向键。如果用户按下按钮,我希望精灵在该方向上移动 5 像素。CurrentPosition
变量是需要修改以修改精灵位置的变量。更新此变量后,必须使用 UpdatePositionMatrix()
方法更新将我们的坐标转换为屏幕空间的矩阵;所有这些都将在 Update()
方法中完成。Update()
方法中的默认代码已经获取了游戏手柄状态。我们只需要对其做出响应。
新的 Update()
方法将如下所示:
public static void Update ()
{
var gamePadData = GamePad.GetData (0);
if( (gamePadData.Buttons & GamePadButtons.Left) !=0)
CurrentPosition.X-=5;
if( (gamePadData.Buttons & GamePadButtons.Right) !=0)
CurrentPosition.X+=5;
if( (gamePadData.Buttons & GamePadButtons.Up) !=0)
CurrentPosition.Y-=5;
if( (gamePadData.Buttons & GamePadButtons.Down) !=0)
CurrentPosition.Y+=5;
UpdatePositionMatrix(CurrentPosition);
}
现在运行程序,如果您使用键盘上的箭头键,您会看到飞船相应地移动。即使我们还不能在真实设备上运行代码(因为我还没有介绍执行该操作的步骤),让我们放入响应模拟摇杆的代码。
播放声音
接下来,我希望程序在用户按下按钮时播放声音。找到一个带有您想播放的声音的波形文件,并以与图像相同的方式将其添加到资源文件夹中。确保波形文件的“构建操作”属性设置为“Context”。在程序中声明一个 Sound 类型的变量(用于保存声音数据)和一个 SoundPlayer(用于保存播放声音的实例)。
我只希望在按钮按下然后释放时播放声音。为了知道按钮从释放状态变为按下状态,我需要记住按钮以前的状态。我将使用另一个 GamePadData 实例来跟踪控制器的整个先前状态。
static Sound mySound;
static SoundPlayer mySoundPlayer;
static GamePadData previousState;
在 Initialize 方法中加载声音。
public static void Initialize ()
{
// Set up the graphics system
graphics = new GraphicsContext ();
//--------------------
// Rest of Initialize() method omitted for clarity
//-------------------
mySound = new Sound("/Application/resources/LOZ_Get_Item.wav");
mySoundPlayer = mySound.CreatePlayer();
}
Update() 方法必须修改为查找将用于触发声音播放的按钮。
if( (( gamePadData.Buttons & GamePadButtons.Cross) != 0)
&& ((previousState.Buttons&GamePadButtons.Cross) == 0)
)
mySoundPlayer.Play();
previousState = gamePadData;
在上面的代码中,我使用 Cross 按钮作为触发声音播放的按钮。这将在键盘上映射到“S”键。再次运行程序并按下“S”按钮。您应该会听到声音播放。
在真实设备上运行
您的代码需要签名才能在真实设备上运行。要执行代码签名,您需要有一个付费账户。签名后,应用程序可以运行长达 90 天。对于这个过程,您需要准备好支付 99 美元购买开发者许可证。当您登录开发门户时,您会看到一个购买开发者许可证的按钮。单击此按钮开始该过程。您将被要求输入您的姓名、地址和电话号码,然后单击“提交”后,您需要等待有人验证您输入的信息。我在周六(假期前一天)输入了这些信息。所以直到下一个工作日(周二),这些信息才被验证。

在信息验证后,您会收到一封电子邮件通知您,然后您可以重新登录门户,按钮现在将更改为允许您支付注册费用。

这是我遇到另一个障碍的地方。尽管我使用了多年的同一张卡号,但此交易并未成功。我的 PlayStation® 钱包里已经有一些钱了。因此,在购买页面上,我看到确认 88.95 将应用于我的卡。

单击“我同意…”后,页面刷新但保持不变。我不确定发生了什么,所以我决定登录我的 PlayStation® 3 并向我的钱包充值。我在 PlayStation® 3 上收到了更有意义的回复。信用卡无法处理。所以我决定尝试另一张卡,并在第二张和第三张卡上收到相同的回复。在第三次失败后,我开车去了 GameStop 购买 PlayStation® 信用额度。后来我才想起,我也可以在亚马逊等在线零售商处购买这些,并且会在线收到卡号。直到几天后,我才收到索尼为何直接购买不成功的解释。我的其中一张卡在其他地方被拒绝,并且在一次尝试交易后几分钟,我的银行打电话给我确认我最近的交易。在恢复我的卡使用能力后,我的银行告知我索尼尝试了一笔 0.00 美元的交易,这被认为是可疑的并引发了欺诈警报。现在,让我们回到正题。
在兑换 PlayStation® 信用额度后,门户网站中的按钮将更改为允许您注册银行信息以接收您应用程序的付款。我不会涵盖这个过程,因为它对于部署 HelloWorld 程序不是必需的。

使用 PSM Publishing Utility
启动 PSM Publish Utility。您需要使用它为您的应用程序创建许可证,以便它可以在真实设备上运行。将您的设备连接到您的计算机。您将从 Metadata 部分开始。

第一页上的大多数默认设置都可以保持不变。但您需要为 Application ID 输入一些内容。我鼓励您探索其他选项卡。评分检查选项卡中的演练尤其有趣。单击“保存”按钮将您的条目保存到 XML 文件。
您的设备有一个与之关联的唯一值,该值将用于创建用于签名代码的密钥。在您的 Vita 上启动 PSM Dev Assist。将您的设备连接到您的计算机,然后在 publishing utility 的 Key Management 部分。设备应自动识别,您应该会在右侧看到设备的种子密钥。单击带有以下图标的按钮将一个新应用程序添加到密钥环。

系统会要求您提供应用程序的 XML 文件。选择您在前一节中创建的 XML 文件。单击“确定”后,您需要使用您的 Sony Entertainment ID 登录。您将在 App Key Ring 中看到您的应用程序。
返回 PSM Studio 并打开 app.xml。在
<?xml version="1.0" encoding="utf-8"?>
<application project_name="CodeProjectHelloWorld" version="1.00" default_locale="en-US">
<runtime_config>
<memory managed_heap_size="32768" resource_heap_size="65536" />
</runtime_config>
<feature_list>
<feature value="GamePad" />
<feature value="Touch" />
</feature_list>
</application>
更改后,您就可以在设备上运行您的程序了。启动 PlayStation® Mobile Development Assistant,将其连接到您的计算机,然后在 PSM Studio 中将部署目标从 Simulator 更改为 PS Vita。您可能需要扩大窗口大小才能显示此下拉列表。

按 [F5],应用程序应在您的设备上运行。有时人们会在这里遇到错误。我遇到的一个错误是“Failed to Install application on device. ExistAppKey: -2147418102”
在 PlayStation® 开发论坛上有一个关于如何解决此错误的 消息。对我最有效的方法是断开设备连接,终止 psmdevice.exe
进程,然后重新连接设备。之后,我就可以运行程序并单步调试代码了。
删除程序
在您的 Vita 上运行 Hello World 后,您可能会想知道如何删除它。目前还没有删除已部署的单个程序的方法。唯一的办法是卸载 PSM Developer Assistance(这将删除其数据),然后重新安装 PSM Developer Assistant。

闭运算
我想写一些更实质性的代码。但在看到将代码部署到设备的过程如此复杂(并且有时感到具有挑战性)之后。所以我认为现在够了。不过,随着工具的发展,我确实计划撰写其他关于 PSM 开发的文章。历史
- 2013 年 1 月 27 日 - 首次发布