A lot of comments, assets update
49
.classpath
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" path="target/generated-sources/annotations">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="ignore_optional_problems" value="true"/>
|
||||||
|
<attribute name="m2e-apt" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
<attribute name="ignore_optional_problems" value="true"/>
|
||||||
|
<attribute name="m2e-apt" value="true"/>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
|
</classpath>
|
34
.project
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>DanmakuProject</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||||
|
</natures>
|
||||||
|
<filteredResources>
|
||||||
|
<filter>
|
||||||
|
<id>1640019058211</id>
|
||||||
|
<name></name>
|
||||||
|
<type>30</type>
|
||||||
|
<matcher>
|
||||||
|
<id>org.eclipse.core.resources.regexFilterMatcher</id>
|
||||||
|
<arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
|
||||||
|
</matcher>
|
||||||
|
</filter>
|
||||||
|
</filteredResources>
|
||||||
|
</projectDescription>
|
4
.settings/org.eclipse.core.resources.prefs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding//src/main/java=UTF-8
|
||||||
|
encoding//src/main/resources=UTF-8
|
||||||
|
encoding/<project>=UTF-8
|
2
.settings/org.eclipse.jdt.apt.core.prefs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.apt.aptEnabled=false
|
9
.settings/org.eclipse.jdt.core.prefs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||||
|
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
|
||||||
|
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||||
|
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
|
||||||
|
org.eclipse.jdt.core.compiler.processAnnotations=disabled
|
||||||
|
org.eclipse.jdt.core.compiler.release=disabled
|
||||||
|
org.eclipse.jdt.core.compiler.source=1.8
|
4
.settings/org.eclipse.m2e.core.prefs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
activeProfiles=
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
resolveWorkspaceProjects=true
|
||||||
|
version=1
|
17
README.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# DanmakuProject
|
||||||
|
|
||||||
|
This is made for a school project. It uses SDL2 under Java using JNA library.
|
||||||
|
The main bindings are from libjsdl which is an attempt to create an interface for SDL2 using JNA
|
||||||
|
but it lacks documentation and demonstration (still a great project). Some SDL2_mixer bindings are
|
||||||
|
also created using JNA, which is not part of the libjsdl. (see Audio.java)
|
||||||
|
|
||||||
|
I hope this proves to be a good demonstration for using libjsdl and JNA.
|
||||||
|
|
||||||
|
## Credits:
|
||||||
|
* Furkan Mudanyalı (Code, planning, execution etc.)
|
||||||
|
* graphLoom, (Music, assets)
|
||||||
|
|
||||||
|
### Further Credits:
|
||||||
|
* libjsdl contributors (especially shinedeveloper for a loot of bindings)
|
||||||
|
* SDL2 contributors
|
||||||
|
* SDL2_mixer contributors
|
BIN
assets/bullets/bullete-big.bmp
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/bullets/bulletp-big.bmp
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/bullets/bulletp.bmp
Normal file
After Width: | Height: | Size: 970 B |
BIN
assets/enemies/guardian.bmp
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/enemies/guardianbig.bmp
Normal file
After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 4.1 KiB |
BIN
assets/shmap.bmp
Before Width: | Height: | Size: 720 KiB After Width: | Height: | Size: 540 KiB |
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 1.5 MiB |
@ -33,7 +33,7 @@ import javax.sound.sampled.AudioSystem;
|
|||||||
/**
|
/**
|
||||||
* <h3>Audio Class</h3>
|
* <h3>Audio Class</h3>
|
||||||
*
|
*
|
||||||
* This class is a native wrapper for SDL2_Mixer,
|
* This class is a native JNA wrapper for SDL2_Mixer,
|
||||||
* it also contains some additional methods
|
* it also contains some additional methods
|
||||||
*
|
*
|
||||||
* @author Furkan Mudanyali
|
* @author Furkan Mudanyali
|
||||||
@ -127,14 +127,21 @@ public class Audio {
|
|||||||
public static int Mix_PlayChannel(int channel, Pointer chunk, int loops){
|
public static int Mix_PlayChannel(int channel, Pointer chunk, int loops){
|
||||||
return Mix_PlayChannelTimed(channel, chunk, loops, -1);
|
return Mix_PlayChannelTimed(channel, chunk, loops, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Returns WAV music length in milliseconds.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
* Credits to mdma from stackoverflow
|
* Credits to mdma from stackoverflow
|
||||||
|
* <p>
|
||||||
* https://stackoverflow.com/a/3009973
|
* https://stackoverflow.com/a/3009973
|
||||||
*
|
*
|
||||||
* @param filepath WAV file path
|
* @param filepath WAV file path
|
||||||
* @return length in milliseconds
|
* @return length in milliseconds, -1 if can't
|
||||||
|
* open file.
|
||||||
*/
|
*/
|
||||||
public static long getMusicLengthInMilliseconds(String filepath){
|
public static long getMusicLengthInMilliseconds(String filepath){
|
||||||
try {
|
try {
|
||||||
|
@ -21,10 +21,25 @@ import java.io.File;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param fileName filename to get path of
|
* <h3>File Loader Class</h3>
|
||||||
* @return path to the asset in the assets folder.
|
*
|
||||||
|
* A class that contains static methods for file path
|
||||||
|
* operations for SDL2. Since SDL2 cannot access the
|
||||||
|
* virtual filesystem inside the .jar package, SDL2 files
|
||||||
|
* must be kept in a seperate folder along with the .jar.
|
||||||
|
*
|
||||||
|
* @author Furkan Mudanyali
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2021-12-01
|
||||||
*/
|
*/
|
||||||
public class FileLoader {
|
public class FileLoader {
|
||||||
|
/**
|
||||||
|
* Returns absolute path to the file from the assets folder.
|
||||||
|
*
|
||||||
|
* @param fileName filename to get path of
|
||||||
|
* @return path to the asset in the assets folder,
|
||||||
|
* null on any kind of I/O error.
|
||||||
|
*/
|
||||||
public static String getFilePath(String fileName){
|
public static String getFilePath(String fileName){
|
||||||
try{
|
try{
|
||||||
String protocol = FileLoader.class.getResource("").getProtocol();
|
String protocol = FileLoader.class.getResource("").getProtocol();
|
||||||
@ -35,7 +50,7 @@ public class FileLoader {
|
|||||||
}
|
}
|
||||||
return System.getProperty("user.dir") + "/assets/" + fileName;
|
return System.getProperty("user.dir") + "/assets/" + fileName;
|
||||||
} catch (Exception e){
|
} catch (Exception e){
|
||||||
return "could not get filepath";
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -20,13 +20,28 @@ package com.fmudanyali;
|
|||||||
import static org.libsdl.api.keyboard.SdlKeyboard.*;
|
import static org.libsdl.api.keyboard.SdlKeyboard.*;
|
||||||
import com.sun.jna.ptr.ByteByReference;
|
import com.sun.jna.ptr.ByteByReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h3>Keyboard Class</h3>
|
||||||
|
*
|
||||||
|
* A class to simplify keyboard functions.
|
||||||
|
*
|
||||||
|
* @author Furkan Mudanyali
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2021-12-01
|
||||||
|
*/
|
||||||
public class Keyboard {
|
public class Keyboard {
|
||||||
|
// Basically array pointer
|
||||||
public static ByteByReference keyboard = new ByteByReference();
|
public static ByteByReference keyboard = new ByteByReference();
|
||||||
|
|
||||||
public static void getKeyboardState(){
|
public static void getKeyboardState(){
|
||||||
keyboard = SDL_GetKeyboardState(null);
|
keyboard = SDL_GetKeyboardState(null);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Checks if given index of the keyboard array is true or not.
|
||||||
|
*
|
||||||
|
* @param key Index of the key in the keyboard array.
|
||||||
|
* @return true if key is pressed, false if not.
|
||||||
|
*/
|
||||||
public static boolean getKeyState(int key){
|
public static boolean getKeyState(int key){
|
||||||
return keyboard.getPointer().getByte(key) == 1;
|
return keyboard.getPointer().getByte(key) == 1;
|
||||||
}
|
}
|
||||||
|
@ -21,23 +21,49 @@ import com.fmudanyali.scenes.*;
|
|||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
import org.libsdl.api.event.events.SDL_Event;
|
import org.libsdl.api.event.events.SDL_Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h3>Main Class</h3>
|
||||||
|
* This is the starting point of the application.
|
||||||
|
*
|
||||||
|
* @author Furkan Mudanyali
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2021-12-01
|
||||||
|
*/
|
||||||
public class Main {
|
public class Main {
|
||||||
public static SDL_Event e = new SDL_Event();
|
public static SDL_Event e = new SDL_Event();
|
||||||
public static boolean exit = false;
|
public static boolean exit = false;
|
||||||
public static Stack<Scene> scenes = new Stack<>();
|
public static Stack<Scene> scenes = new Stack<>();
|
||||||
public static void main(String[] args) throws Exception{
|
public static void main(String[] args) throws Exception{
|
||||||
|
/*
|
||||||
|
Restart JVM if on macOS and it hasn't been launched
|
||||||
|
with -XStartOnFirstThread arg. Needed for SDL to be
|
||||||
|
able to create a window and show it on macOS as macOS
|
||||||
|
ignores windows that are not opened by the first thread
|
||||||
|
of the application for safety measures. Took me days
|
||||||
|
to track the bug.
|
||||||
|
*/
|
||||||
if (RestartJVM.restartJVM()) {
|
if (RestartJVM.restartJVM()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open audio device with basic defaults.
|
||||||
Audio.Mix_OpenAudio(44100, Audio.MIX_DEFAULT_FORMAT, 2, 2048);
|
Audio.Mix_OpenAudio(44100, Audio.MIX_DEFAULT_FORMAT, 2, 2048);
|
||||||
|
// Push main menu to the scenes stack.
|
||||||
scenes.push(new MainMenu());
|
scenes.push(new MainMenu());
|
||||||
|
|
||||||
|
// Each loop means a frame of the game.
|
||||||
while(!scenes.empty() && !exit){
|
while(!scenes.empty() && !exit){
|
||||||
|
// Calculate delta time, between last frame
|
||||||
|
// and this frame.
|
||||||
Time.Tick();
|
Time.Tick();
|
||||||
Keyboard.getKeyboardState();
|
Keyboard.getKeyboardState();
|
||||||
|
// Call the loop function of the scene on the
|
||||||
|
// top of the scenes stack.
|
||||||
scenes.peek().loop();
|
scenes.peek().loop();
|
||||||
}
|
}
|
||||||
|
// Exit is set to true manually to make sure the existing threads
|
||||||
|
// are stopped, not really necessary as System.exit should close
|
||||||
|
// those lingering threads but better be safe than sorry.
|
||||||
exit = true;
|
exit = true;
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
}
|
}
|
||||||
|
@ -33,24 +33,50 @@ import static org.libsdl.api.hints.SdlHintsConst.*;
|
|||||||
import static org.libsdl.api.hints.SdlHints.*;
|
import static org.libsdl.api.hints.SdlHints.*;
|
||||||
import static org.libsdl.api.render.SdlRender.SDL_RenderSetLogicalSize;
|
import static org.libsdl.api.render.SdlRender.SDL_RenderSetLogicalSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h3>Render Class</h3>
|
||||||
|
* This class contains and initializes the SDL_Window and SDL_Renderer pointers,
|
||||||
|
* it also has a method to create a continuous background from given texture.
|
||||||
|
*
|
||||||
|
* @author Furkan Mudanyali
|
||||||
|
* @version 0.2.0
|
||||||
|
* @since 2021-12-01
|
||||||
|
*/
|
||||||
public class Render {
|
public class Render {
|
||||||
public static SDL_Window window;
|
public static SDL_Window window;
|
||||||
public static SDL_Renderer renderer;
|
public static SDL_Renderer renderer;
|
||||||
public static int bgw, bgh;
|
public static int bgw, bgh;
|
||||||
|
|
||||||
|
// Initialize SDL_Window and SDL_Renderer
|
||||||
static {
|
static {
|
||||||
|
// Create window
|
||||||
window = SDL_CreateWindow("DanmakuProject SDL2",
|
window = SDL_CreateWindow("DanmakuProject SDL2",
|
||||||
SDL_WINDOWPOS_CENTERED(), SDL_WINDOWPOS_CENTERED(),
|
SDL_WINDOWPOS_CENTERED(), SDL_WINDOWPOS_CENTERED(),
|
||||||
WIDTH, HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
|
WIDTH, HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
|
||||||
|
|
||||||
|
// Hints for the renderer
|
||||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
|
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
|
||||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
|
||||||
|
|
||||||
|
// Create renderer
|
||||||
renderer =
|
renderer =
|
||||||
SDL_CreateRenderer(Render.window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||||
|
|
||||||
|
// Make content scale according to the window size while maintaining the aspect ratio.
|
||||||
SDL_RenderSetLogicalSize(renderer, Screen.WIDTH, Screen.HEIGHT);
|
SDL_RenderSetLogicalSize(renderer, Screen.WIDTH, Screen.HEIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new row x col texture
|
||||||
|
* from the given texture by stitching
|
||||||
|
* it together.
|
||||||
|
*
|
||||||
|
* @param renderer Renderer to be used
|
||||||
|
* @param texture Texture to be repeated
|
||||||
|
* @param cols How many columns of the texture
|
||||||
|
* @param rows How many rows of the texture
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public static SDL_Texture createBackgroundFromTexture(
|
public static SDL_Texture createBackgroundFromTexture(
|
||||||
SDL_Renderer renderer, SDL_Texture texture, int cols, int rows
|
SDL_Renderer renderer, SDL_Texture texture, int cols, int rows
|
||||||
){
|
){
|
||||||
|
@ -25,10 +25,22 @@ import java.io.InputStreamReader;
|
|||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Credits to kappa From
|
* <h3>RestartJVM Class</h3>
|
||||||
|
*
|
||||||
|
* Has a single function that restarts JVM on macOS
|
||||||
|
* if JVM is not launched with -XstartOnFirstThread arg
|
||||||
|
* and launches it with it.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* Credits to kappa from jvm-gaming.org
|
||||||
|
* <p>
|
||||||
* https://jvm-gaming.org/t/starting-jvm-on-mac-with-xstartonfirstthread-programmatically/57547
|
* https://jvm-gaming.org/t/starting-jvm-on-mac-with-xstartonfirstthread-programmatically/57547
|
||||||
|
*
|
||||||
|
* @author kappa
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2016-08-26
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class RestartJVM {
|
public class RestartJVM {
|
||||||
public static boolean restartJVM() {
|
public static boolean restartJVM() {
|
||||||
|
|
||||||
|
@ -27,6 +27,16 @@ import com.fmudanyali.characters.Player;
|
|||||||
|
|
||||||
import static org.libsdl.api.render.SdlRender.*;
|
import static org.libsdl.api.render.SdlRender.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h3>Screen Class</h3>
|
||||||
|
*
|
||||||
|
* This class contains information about the game screen
|
||||||
|
* It also has a method to set its background texture, and scroll it.
|
||||||
|
*
|
||||||
|
* @author Furkan Mudanyali
|
||||||
|
* @version 0.2.0
|
||||||
|
* @since 2021-12-08
|
||||||
|
*/
|
||||||
public class Screen {
|
public class Screen {
|
||||||
public static final int WIDTH = 960;
|
public static final int WIDTH = 960;
|
||||||
public static final int HEIGHT = 540;
|
public static final int HEIGHT = 540;
|
||||||
@ -38,6 +48,7 @@ public class Screen {
|
|||||||
public static SDL_Texture tile, background, wallpaper;
|
public static SDL_Texture tile, background, wallpaper;
|
||||||
public static SDL_Surface tempSurface;
|
public static SDL_Surface tempSurface;
|
||||||
|
|
||||||
|
// Calculate positions and load wallpaper image.
|
||||||
static {
|
static {
|
||||||
canvas.x = 12;
|
canvas.x = 12;
|
||||||
canvas.y = 0;
|
canvas.y = 0;
|
||||||
@ -54,6 +65,10 @@ public class Screen {
|
|||||||
tempSurface = null;
|
tempSurface = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the given filepath to the background.
|
||||||
|
* @param filename file path of the image.
|
||||||
|
*/
|
||||||
public static void makeBackground(String filename){
|
public static void makeBackground(String filename){
|
||||||
// Load tile
|
// Load tile
|
||||||
tempSurface = SDL_LoadBMP(FileLoader.getFilePath(filename));
|
tempSurface = SDL_LoadBMP(FileLoader.getFilePath(filename));
|
||||||
@ -64,6 +79,12 @@ public class Screen {
|
|||||||
background = Render.createBackgroundFromTexture(Render.renderer, tile, 1, 2);
|
background = Render.createBackgroundFromTexture(Render.renderer, tile, 1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scrolls the background vertically and horizontally.
|
||||||
|
* Horizontal scroll is based on the player position to
|
||||||
|
* create a parallax effect.
|
||||||
|
* @param player
|
||||||
|
*/
|
||||||
public static void scroll(Player player){
|
public static void scroll(Player player){
|
||||||
canvas.y = Math.floorMod(canvas.y - (int)(Time.deltaTime * 0.1), Render.bgh - canvas.h);
|
canvas.y = Math.floorMod(canvas.y - (int)(Time.deltaTime * 0.1), Render.bgh - canvas.h);
|
||||||
canvas.x = (int)(12 + (player.position.x - 464)/13);
|
canvas.x = (int)(12 + (player.position.x - 464)/13);
|
||||||
|
@ -19,6 +19,16 @@ package com.fmudanyali;
|
|||||||
|
|
||||||
import static org.libsdl.api.timer.SdlTimer.*;
|
import static org.libsdl.api.timer.SdlTimer.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h3>Time Class</h3>
|
||||||
|
*
|
||||||
|
* Calculates the time difference between this frame
|
||||||
|
* and last frame, deltatime.
|
||||||
|
*
|
||||||
|
* @author Furkan Mudanyali
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2021-12-03
|
||||||
|
*/
|
||||||
public class Time {
|
public class Time {
|
||||||
public static int lastTime = 0;
|
public static int lastTime = 0;
|
||||||
public static int currentTime = 0;
|
public static int currentTime = 0;
|
||||||
|
@ -28,13 +28,27 @@ import org.libsdl.api.surface.SDL_Surface;
|
|||||||
import static org.libsdl.api.surface.SdlSurface.*;
|
import static org.libsdl.api.surface.SdlSurface.*;
|
||||||
import static org.libsdl.api.render.SdlRender.*;
|
import static org.libsdl.api.render.SdlRender.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h3>PlayerBullet Class</h3>
|
||||||
|
*
|
||||||
|
* This class is for player made projectiles
|
||||||
|
* that advance forward in the screen and explode
|
||||||
|
* on enemy contact (to be implemented).
|
||||||
|
*
|
||||||
|
* @author Furkan Mudanyali
|
||||||
|
* @version 0.4.0
|
||||||
|
* @since 2021-12-22
|
||||||
|
*/
|
||||||
public class PlayerBullet {
|
public class PlayerBullet {
|
||||||
|
// Load the texture
|
||||||
public static SDL_Surface tempSurface = SDL_LoadBMP(FileLoader.getFilePath("player/bullet.bmp"));
|
public static SDL_Surface tempSurface = SDL_LoadBMP(FileLoader.getFilePath("player/bullet.bmp"));
|
||||||
public static SDL_Texture texture = SDL_CreateTextureFromSurface(Render.renderer, tempSurface);
|
public static SDL_Texture texture = SDL_CreateTextureFromSurface(Render.renderer, tempSurface);
|
||||||
public SDL_Rect position = new SDL_Rect();
|
public SDL_Rect position = new SDL_Rect();
|
||||||
public int width = 5;
|
public int width = 5;
|
||||||
public int height = 12;
|
public int height = 12;
|
||||||
|
|
||||||
|
// Set the bullet position relative to the player position and given
|
||||||
|
// offsets.
|
||||||
public PlayerBullet(SDL_Rect playerPos, int xOffset, int yOffset){
|
public PlayerBullet(SDL_Rect playerPos, int xOffset, int yOffset){
|
||||||
position.w = width;
|
position.w = width;
|
||||||
position.h = height;
|
position.h = height;
|
||||||
@ -42,6 +56,9 @@ public class PlayerBullet {
|
|||||||
position.y = playerPos.y - 6 - 3 - yOffset;
|
position.y = playerPos.y - 6 - 3 - yOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advances the bullet on the screen.
|
||||||
|
*/
|
||||||
public void fly(){
|
public void fly(){
|
||||||
position.y = position.y - (int)(Time.deltaTime * 0.5);
|
position.y = position.y - (int)(Time.deltaTime * 0.5);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,16 @@ import org.libsdl.api.rect.SDL_Rect;
|
|||||||
import org.libsdl.api.render.SDL_Texture;
|
import org.libsdl.api.render.SDL_Texture;
|
||||||
import org.libsdl.api.surface.SDL_Surface;
|
import org.libsdl.api.surface.SDL_Surface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h3>Character Superclass</h3>
|
||||||
|
*
|
||||||
|
* This class provides essentials that a character
|
||||||
|
* should require.
|
||||||
|
*
|
||||||
|
* @author Furkan Mudanyali
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2021-12-08
|
||||||
|
*/
|
||||||
public class Character {
|
public class Character {
|
||||||
public SDL_Rect position = new SDL_Rect();
|
public SDL_Rect position = new SDL_Rect();
|
||||||
public SDL_Texture texture = new SDL_Texture();
|
public SDL_Texture texture = new SDL_Texture();
|
||||||
|
@ -18,5 +18,5 @@
|
|||||||
package com.fmudanyali.characters;
|
package com.fmudanyali.characters;
|
||||||
|
|
||||||
public class Enemy extends Character {
|
public class Enemy extends Character {
|
||||||
|
// TODO: Implement
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,19 @@ import static org.libsdl.api.surface.SdlSurface.*;
|
|||||||
import static org.libsdl.api.render.SdlRender.*;
|
import static org.libsdl.api.render.SdlRender.*;
|
||||||
import static org.libsdl.api.scancode.SDL_Scancode.*;
|
import static org.libsdl.api.scancode.SDL_Scancode.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h3>Player Class</h3>
|
||||||
|
*
|
||||||
|
* This class extends on the character class and has all the
|
||||||
|
* bells and whistles such as handling movement, shooting bullets,
|
||||||
|
* animated sprites etc.
|
||||||
|
*
|
||||||
|
* @author Furkan Mudanyali
|
||||||
|
* @version 0.9.0
|
||||||
|
* @since 2021-12-08
|
||||||
|
*/
|
||||||
public class Player extends Character {
|
public class Player extends Character {
|
||||||
|
// Variables
|
||||||
public int lives;
|
public int lives;
|
||||||
public double speed;
|
public double speed;
|
||||||
public SDL_Texture propeller;
|
public SDL_Texture propeller;
|
||||||
@ -40,14 +52,16 @@ public class Player extends Character {
|
|||||||
public int frame = 0;
|
public int frame = 0;
|
||||||
public int roll = 0;
|
public int roll = 0;
|
||||||
public int cooldown = 0;
|
public int cooldown = 0;
|
||||||
|
// New position rectangles for additional parts
|
||||||
public SDL_Rect propellerPos = new SDL_Rect();
|
public SDL_Rect propellerPos = new SDL_Rect();
|
||||||
public SDL_Rect shooterPos = new SDL_Rect();
|
public SDL_Rect shooterPos = new SDL_Rect();
|
||||||
|
// These rectangles are used for shifting
|
||||||
|
// sprite animation.
|
||||||
public SDL_Rect shipFrame = new SDL_Rect();
|
public SDL_Rect shipFrame = new SDL_Rect();
|
||||||
public SDL_Rect propellerFrame = new SDL_Rect();
|
public SDL_Rect propellerFrame = new SDL_Rect();
|
||||||
public SDL_Rect shooterFrame = new SDL_Rect();
|
public SDL_Rect shooterFrame = new SDL_Rect();
|
||||||
|
|
||||||
|
// Load the textures and set the positions.
|
||||||
public Player(){
|
public Player(){
|
||||||
lives = 3;
|
lives = 3;
|
||||||
tempSurface = SDL_LoadBMP(FileLoader.getFilePath("player/ship.bmp"));
|
tempSurface = SDL_LoadBMP(FileLoader.getFilePath("player/ship.bmp"));
|
||||||
@ -167,7 +181,8 @@ public class Player extends Character {
|
|||||||
propellerFrame.x = 10;
|
propellerFrame.x = 10;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Not a switch as DeltaTime does not provide
|
||||||
|
// linear increase.
|
||||||
if(roll > 0){
|
if(roll > 0){
|
||||||
shipFrame.x = shipFrame.y = 0;
|
shipFrame.x = shipFrame.y = 0;
|
||||||
} if(roll > 15){
|
} if(roll > 15){
|
||||||
@ -178,17 +193,23 @@ public class Player extends Character {
|
|||||||
} if(roll > 45){
|
} if(roll > 45){
|
||||||
shipFrame.x = 32;
|
shipFrame.x = 32;
|
||||||
}
|
}
|
||||||
|
// Frame is kept in 600 instead of 60
|
||||||
|
// to be accurate with the varying framerate
|
||||||
|
// animations.
|
||||||
frame = (frame+10)%600;
|
frame = (frame+10)%600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes positions depending on the keyboard state.
|
||||||
|
*/
|
||||||
public void movement(){
|
public void movement(){
|
||||||
|
// If shift is pressed, slow down the player.
|
||||||
if(Keyboard.getKeyState(SDL_SCANCODE_LSHIFT)){
|
if(Keyboard.getKeyState(SDL_SCANCODE_LSHIFT)){
|
||||||
speed = 1.5;
|
speed = 1.5;
|
||||||
}else{
|
}else{
|
||||||
speed = 2;
|
speed = 2;
|
||||||
}
|
}
|
||||||
|
// Bunch of mathematical mumbo jumbo.
|
||||||
if(Keyboard.getKeyState(SDL_SCANCODE_A) | Keyboard.getKeyState(SDL_SCANCODE_LEFT)){
|
if(Keyboard.getKeyState(SDL_SCANCODE_A) | Keyboard.getKeyState(SDL_SCANCODE_LEFT)){
|
||||||
position.x = Math.max(position.x - (int)(speed * Time.deltaTime * 0.1), Screen.canvasPos.x - 5);
|
position.x = Math.max(position.x - (int)(speed * Time.deltaTime * 0.1), Screen.canvasPos.x - 5);
|
||||||
shooterPos.x = Math.max(shooterPos.x - (int)(speed * Time.deltaTime * 0.1), Screen.canvasPos.x + 16 - 4 - 5);
|
shooterPos.x = Math.max(shooterPos.x - (int)(speed * Time.deltaTime * 0.1), Screen.canvasPos.x + 16 - 4 - 5);
|
||||||
@ -227,7 +248,8 @@ public class Player extends Character {
|
|||||||
Game.playerBullets.add(new PlayerBullet(position, 17, 0));
|
Game.playerBullets.add(new PlayerBullet(position, 17, 0));
|
||||||
Game.playerBullets.add(new PlayerBullet(position, 21, 6));
|
Game.playerBullets.add(new PlayerBullet(position, 21, 6));
|
||||||
Game.playerBullets.add(new PlayerBullet(position, 25, 9));
|
Game.playerBullets.add(new PlayerBullet(position, 25, 9));
|
||||||
cooldown = 1;
|
// Cooldown is used to prevent overshooting.
|
||||||
|
cooldown = 3;
|
||||||
}
|
}
|
||||||
cooldown = Math.max(cooldown -1, 0);
|
cooldown = Math.max(cooldown -1, 0);
|
||||||
}
|
}
|
||||||
|
@ -33,51 +33,85 @@ import java.util.ArrayList;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h3>Game Class</h3>
|
||||||
|
*
|
||||||
|
* This class extends on the Scene class and is mainly
|
||||||
|
* the core of the game.
|
||||||
|
*
|
||||||
|
* @author Furkan Mudanyali
|
||||||
|
* @version 0.1.0
|
||||||
|
* @since 2021-12-04
|
||||||
|
*/
|
||||||
public class Game extends Scene {
|
public class Game extends Scene {
|
||||||
|
// Variables
|
||||||
public static boolean escPressed = false;
|
public static boolean escPressed = false;
|
||||||
int kek = 0;
|
int kek = 0;
|
||||||
private Player player = new Player();
|
private Player player = new Player();
|
||||||
|
// Keeping track of the shots fired
|
||||||
public static List<PlayerBullet> playerBullets = new ArrayList<>();
|
public static List<PlayerBullet> playerBullets = new ArrayList<>();
|
||||||
|
// Thread for bg music
|
||||||
private static Thread thread;
|
private static Thread thread;
|
||||||
|
// Create a runnable for the thread
|
||||||
private static Runnable runnable = new Runnable() {
|
private static Runnable runnable = new Runnable() {
|
||||||
public void run(){
|
public void run(){
|
||||||
try {
|
try {
|
||||||
Mix_PlayMusic(Mix_LoadMUS(FileLoader.getFilePath("80-search-intro.wav")), 1);
|
Mix_PlayMusic(Mix_LoadMUS(FileLoader.getFilePath("80-search-intro.wav")), 1);
|
||||||
|
// Sleeping for the duration of the intro instead of checking if it stopped on a
|
||||||
|
// loop saves a loooot of clock cycles.
|
||||||
Thread.sleep(getMusicLengthInMilliseconds(FileLoader.getFilePath("80-search-intro.wav")));
|
Thread.sleep(getMusicLengthInMilliseconds(FileLoader.getFilePath("80-search-intro.wav")));
|
||||||
Mix_PlayMusic(Mix_LoadMUS(FileLoader.getFilePath("80-search-loop.wav")), -1);
|
Mix_PlayMusic(Mix_LoadMUS(FileLoader.getFilePath("80-search-loop.wav")), -1);
|
||||||
|
// Kill self because the last music will loop forever
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
return;
|
return;
|
||||||
} catch(Exception e){
|
} catch(Exception e){
|
||||||
// Will throw sleep interrupted, which is what we want.
|
// Will throw sleep interrupted, which is exactly what we want.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public Game(){
|
public Game(){
|
||||||
|
// Create the looping background
|
||||||
Screen.makeBackground("shmap.bmp");
|
Screen.makeBackground("shmap.bmp");
|
||||||
|
// Set music volume to max
|
||||||
Mix_VolumeMusic(128);
|
Mix_VolumeMusic(128);
|
||||||
|
// Try reading thread state, will throw exception for
|
||||||
|
// the first time as the thread will not be initialized
|
||||||
|
// at that time, which is perfectly expected.
|
||||||
try {
|
try {
|
||||||
|
// Interrupt the thread if its running, this is needed
|
||||||
|
// to keep only one thread alive at a time because if
|
||||||
|
// the game is immediately quitted and a new game started,
|
||||||
|
// the music will not get confused.
|
||||||
if(thread.getState() != Thread.State.TERMINATED){
|
if(thread.getState() != Thread.State.TERMINATED){
|
||||||
thread.interrupt();
|
thread.interrupt();
|
||||||
}
|
}
|
||||||
} catch (Exception e){
|
} catch (Exception e){
|
||||||
//
|
// Do nothing as I expect this behavior.
|
||||||
}
|
}
|
||||||
|
// Overwrite the the TERMINATED (or not initialized)
|
||||||
|
// thread with a new thread with our runnable.
|
||||||
thread = new Thread(runnable);
|
thread = new Thread(runnable);
|
||||||
|
// Start the thread.
|
||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loop(){
|
public void loop(){
|
||||||
|
// Handle events first
|
||||||
while(SDL_PollEvent(Main.e) != 0){
|
while(SDL_PollEvent(Main.e) != 0){
|
||||||
switch(Main.e.type){
|
switch(Main.e.type){
|
||||||
|
// If close button on the window is pressed.
|
||||||
case SDL_QUIT:
|
case SDL_QUIT:
|
||||||
Main.exit = true;
|
Main.exit = true;
|
||||||
break;
|
break;
|
||||||
|
// If a key is pressed
|
||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
switch(Main.e.key.keysym.sym){
|
switch(Main.e.key.keysym.sym){
|
||||||
|
// If ESC is pressed, push pause menu to the scenes stack
|
||||||
case SDLK_ESCAPE:
|
case SDLK_ESCAPE:
|
||||||
|
// Needed to execute it only once per conventional key press
|
||||||
if(!escPressed){
|
if(!escPressed){
|
||||||
Main.scenes.push(new PauseMenu());
|
Main.scenes.push(new PauseMenu());
|
||||||
Mix_VolumeMusic(30);
|
Mix_VolumeMusic(30);
|
||||||
@ -86,6 +120,7 @@ public class Game extends Scene {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
// If a key is released
|
||||||
case SDL_KEYUP:
|
case SDL_KEYUP:
|
||||||
switch(Main.e.key.keysym.sym){
|
switch(Main.e.key.keysym.sym){
|
||||||
case SDLK_ESCAPE:
|
case SDLK_ESCAPE:
|
||||||
@ -96,27 +131,31 @@ public class Game extends Scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do player movement thing and scroll the screen
|
||||||
player.movement();
|
player.movement();
|
||||||
Screen.scroll(player);
|
Screen.scroll(player);
|
||||||
|
// Copying stuff to the renderer
|
||||||
SDL_RenderClear(renderer);
|
SDL_RenderClear(renderer);
|
||||||
SDL_RenderCopy(renderer, Screen.wallpaper, null, null);
|
SDL_RenderCopy(renderer, Screen.wallpaper, null, null);
|
||||||
SDL_RenderCopy(renderer, Screen.background, Screen.canvas, Screen.canvasPos);
|
SDL_RenderCopy(renderer, Screen.background, Screen.canvas, Screen.canvasPos);
|
||||||
SDL_RenderCopy(renderer, player.texture, player.shipFrame, player.position);
|
SDL_RenderCopy(renderer, player.texture, player.shipFrame, player.position);
|
||||||
SDL_RenderCopy(renderer, player.propeller, player.propellerFrame, player.propellerPos);
|
SDL_RenderCopy(renderer, player.propeller, player.propellerFrame, player.propellerPos);
|
||||||
SDL_RenderCopy(renderer, player.shooter, player.shooterFrame, player.shooterPos);
|
SDL_RenderCopy(renderer, player.shooter, player.shooterFrame, player.shooterPos);
|
||||||
|
// Iterate over the bullets on the screen
|
||||||
for(Iterator<PlayerBullet> bulletIterator = playerBullets.iterator(); bulletIterator.hasNext();){
|
for(Iterator<PlayerBullet> bulletIterator = playerBullets.iterator(); bulletIterator.hasNext();){
|
||||||
PlayerBullet b = bulletIterator.next();
|
PlayerBullet b = bulletIterator.next();
|
||||||
|
// Move the bullet
|
||||||
b.fly();
|
b.fly();
|
||||||
|
// If the bullet is out of the screen, destroy it, else copy it to the renderer.
|
||||||
if(b.position.y < 30){
|
if(b.position.y < 30){
|
||||||
bulletIterator.remove();
|
bulletIterator.remove();
|
||||||
} else {
|
} else {
|
||||||
SDL_RenderCopy(renderer, PlayerBullet.texture, null, b.position);
|
SDL_RenderCopy(renderer, PlayerBullet.texture, null, b.position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Present the renderer to the window.
|
||||||
SDL_RenderPresent(renderer);
|
SDL_RenderPresent(renderer);
|
||||||
|
// Advance player sprite animation
|
||||||
player.shiftFrame();
|
player.shiftFrame();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,6 +17,20 @@
|
|||||||
|
|
||||||
package com.fmudanyali.scenes;
|
package com.fmudanyali.scenes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h3>Scene Class</h3>
|
||||||
|
*
|
||||||
|
* This class is an abstract for other scenes and makes able to
|
||||||
|
* keep all the different kind of scenes on a single stack.
|
||||||
|
*
|
||||||
|
* @author Furkan Mudanyali
|
||||||
|
* @version 1.0.0
|
||||||
|
* @since 2021-12-04
|
||||||
|
*/
|
||||||
public abstract class Scene {
|
public abstract class Scene {
|
||||||
|
/**
|
||||||
|
* By design, this function will be
|
||||||
|
* executed each game frame.
|
||||||
|
*/
|
||||||
public abstract void loop();
|
public abstract void loop();
|
||||||
}
|
}
|