所以基本上,我正在尝试编写一个 2d 骨骼动画编辑器以及一个框架,以便在游戏中轻松使用在编辑器中创建的相同动画。最大的问题是我以前没有写过 XML 解析器,所以我有一种感觉,我可能做错了什么,但我不确定它是什么。
这是我格式的示例文件:
<skeleton name="skeleton1">
<bones>
<bone name="bone1" id="1" textureLoc="/imgs/test.png">
<position>x,y</position>
<rotation>float</rotation>
<scale>float</scale>
</bone>
<bone name="bone2" id="2" textureLoc="/imgs/text2.png">
<position>x,y</position>
<rotation>float</rotation>
<scale>float</scale>
</bone>
</bones>
<animation name="anim1">
<keyframe frameToPlay="1">
<bone id="1" transPosition="x,y" transRotation="float" transScale="float">
</bone>
<bone id="2" transPosition="x,y" transRotation="float" transScale="float">
</bone>
</keyframe>
<keyframe frameToPlay="50">
<bone id="1" transPosition="x,y" transRotation="float" transScale="float">
</bone>
<bone id="2" transPosition="x,y" transRotation="float" transScale="float">
</bone>
</keyframe>
</animation>
<animation name="anim2">
<keyframe frameToPlay="1">
<bone id="1" transPosition="x,y" transRotation="float" transScale="float">
</bone>
<bone id="2" transPosition="x,y" transRotation="float" transScale="float">
</bone>
</keyframe>
<keyframe frameToPlay="50">
<bone id="1" transPosition="x,y" transRotation="float" transScale="float">
</bone>
<bone id="2" transPosition="x,y" transRotation="float" transScale="float">
</bone>
</keyframe>
</animation>
</skeleton>
以下是我组织这个系统的方式的描述,如果它使我的代码更易于理解:
AnimationSystem – Management interface for all animations in game, handles updating of each skeleton
--List of skeletons
---Skeletons represent each object
--Update method that updates each skeleton with the game’s current frame
Animation
--List of keyframes
--List of frames, in ascending order, pulled from the list of keyframes
---Order index in list corresponds to order index in list of keyframes
---Should help to speed things up a little bit, no need to search every keyframe to determine which one has a frame value matching the one that is currently needed.
---Will be determined by a set up method after assets are loaded, but before the animation can be used.
--Method to return a keyframe that corresponds to a frame passed to the method
Skeleton
--List of animations
--List of bones
--Value to hold the currently selected animation
--Value to hold the current frame in the game
--Value to hold the name of the skeleton
--Upon moving to a new frame, searches currently selected animation’s list of frames to see if a keyframe should be “played” this frame. If a keyframe exists that should be played at this frame, it should go through each bone in its list of bones, and set the properties of each bone to the properties listed in the keyframe according to the bone’s id.
Bone
--Value to hold the stationary position
--Value to hold the stationary rotation
--Value to hold the stationary scale
--Value to hold the transformed position
--Value to hold the transformed rotation
--Value to hold the transformed scale
--Image value that holds the texture of the bone
--Value to hold the id of the bone within its skeleton
--Value to hold the name of the bone (Used in the editor)
--Method that takes 3 values (position, rotation and scale in that order) and set’s its own transformed pos, rot and scale values to those passed in.
Keyframe
--List of scale values
--List of position values
--List of rotation values
--List of bone ids
---Index of bone id in list matches up to index in scale, position and rotation lists so that the properties can be associated with the bone
--Time value at which the keyframe will be played
--Method to return the scale value of a bone as determined by the bone id passed in
--Method to return the position value of a bone as determined by the bone id passed in
--Method to return the rotation value of a bone as determined by the bone id passed in
我为尝试解析此代码而编写的相关方法如下:
public static Skeleton parseSkeletonFile(String location) throws FileNotFoundException, IOException
{
try
{
Skeleton skelToReturn = null;
File fileToLoad = new File(location);
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(fileToLoad);
doc.getDocumentElement().normalize();
NodeList skelList = doc.getElementsByTagName("skeleton");
for (int counter = 0; counter < skelList.getLength(); counter++)
{
Node skelNode = skelList.item(counter);
Element skelElement = (Element)skelNode;
skelToReturn = new Skeleton(skelElement.getAttribute("name"));
NodeList allNodes = skelNode.getChildNodes();
for (int counter2 = 0; counter2 < allNodes.getLength(); counter2++)
{
Node currentNode = allNodes.item(counter2);
if (currentNode.getNodeName() == "bones")
{
NodeList bonesNL = currentNode.getChildNodes();
for (int counter3 = 0; counter3 < bonesNL.getLength(); counter3++)
{
Node currentBone = bonesNL.item(counter3);
Element currentBoneE = (Element)currentBone;
Bone bone = new Bone(
currentBoneE.getAttribute("name"),
new Point(
Float.valueOf(currentBoneE.getElementsByTagName("position").item(0).getTextContent().split(",")[0]),
Float.valueOf(currentBoneE.getElementsByTagName("position").item(0).getTextContent().split(",")[1])
),
Float.valueOf(currentBoneE.getElementsByTagName("rotation").item(0).getTextContent()),
Float.valueOf(currentBoneE.getElementsByTagName("scale").item(0).getTextContent()),
Integer.valueOf(currentBoneE.getAttribute("id"))
);
bone.setImage(new Image(currentBoneE.getAttribute("textureLoc")));
skelToReturn.bones.add(bone);
}
}
if (currentNode.getNodeName() == "animation")
{
NodeList animsNL = currentNode.getChildNodes();
for (int counter4 = 0; counter4 < animsNL.getLength(); counter4++)
{
Node currentAnim = animsNL.item(counter4);
Element currentAnimE = (Element)currentAnim;
Animation animation = new Animation(currentAnimE.getAttribute("name"));
NodeList keyframesNL = currentAnim.getChildNodes();
for (int counter5 = 0; counter5 < keyframesNL.getLength(); counter5++)
{
Node currentKeyframe = keyframesNL.item(counter5);
Element currentKeyframeE = (Element)currentKeyframe;
Keyframe keyframe = new Keyframe(Integer.valueOf(currentKeyframeE.getAttribute("frameToPlay")));
NodeList kfBonesNL = currentKeyframe.getChildNodes();
for (int counter6 = 0; counter6 < kfBonesNL.getLength(); counter6++)
{
Node currentBoneTrans = kfBonesNL.item(counter6);
Element currentBoneTransE = (Element)currentBoneTrans;
keyframe.boneIDs.add(Integer.valueOf(currentBoneTransE.getAttribute("id")));
keyframe.positions.add(new Point(Float.valueOf(currentBoneTransE.getAttribute("transPosition").split(",")[0]), Float.valueOf(currentBoneTransE.getAttribute("transPosition").split(",")[1])));
keyframe.rotations.add(Float.valueOf(currentBoneTransE.getAttribute("transRotation")));
keyframe.scales.add(Float.valueOf(currentBoneTransE.getAttribute("transScale")));
}
animation.keyframes.add(keyframe);
animation.frames.add(keyframe.getTimeToBePlayed());
}
skelToReturn.animations.add(animation);
skelToReturn.animationNames.add(animation.name);
}
}
}
}
if (skelToReturn != null)
return skelToReturn;
else
return null;
} catch (Exception e)
{
e.printStackTrace();
}
return null;
}
这是我用来测试解析器的代码片段:
AnimationSystem animator = new AnimationSystem();
try {
skeleton = animator.parseSkeletonFile(getClass().getResource("SkeletonTest.xml").getPath());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (skeleton != null)
{
System.out.println(skeleton.name);
}
这是在 Slick2D 中构建的简单游戏的 init 方法中。我尝试加载的文件 SkeletonTest.xml 与调用解析方法的类位于同一个包中。它的内容很简单:
<skeleton name="TestSkeleton">
</skeleton>
这是我收到的完整错误消息:
java.io.FileNotFoundException: D:\Eclipse%20Workspace\2dSkeletalAnimator\bin\org\jason\animatorTests\SkeletonTest.xml (The system cannot find the path specified)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(Unknown Source)
at java.io.FileInputStream.<init>(Unknown Source)
at sun.net.www.protocol.file.FileURLConnection.connect(Unknown Source)
at sun.net.www.protocol.file.FileURLConnection.getInputStream(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.determineDocVersion(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(Unknown Source)
at javax.xml.parsers.DocumentBuilder.parse(Unknown Source)
at org.jason.skeletalanimator.AnimationSystem.parseSkeletonFile(AnimationSystem.java:42)
at org.jason.animatorTests.AnimatorTest1.init(AnimatorTest1.java:32)
at org.newdawn.slick.AppGameContainer.setup(AppGameContainer.java:433)
at org.newdawn.slick.AppGameContainer.start(AppGameContainer.java:357)
at org.jason.animatorTests.AnimatorTest1.main(AnimatorTest1.java:65)
抱歉,有些笼统的问题和所需的背景信息量,但我已经尝试了几乎所有的方法来让它工作,而不是重写解析方法的主体。我宁愿不再经历那个烂摊子。所以我希望有更多经验从 XML 样式文件加载数据的人可以在这里帮助我。我感谢任何提前给我时间的人。