I recently uploaded my app to the google play store Helicopter Combat
But every now and then it currupts. I got the error report here:
java.lang.ArrayIndexOutOfBoundsException: length=3; index=3
at de.krissini.server.Animation.getImage(Animation.java:36)
That's the refered code:
public class Animation {
private Bitmap[] frames;
private int currentFrame;
private long startTime, delay;
private boolean playedOnce;
public void setFrames(Bitmap[] frames){
this.frames = frames;
currentFrame = 0;
startTime = System.nanoTime();
}
public void setDelay(long d){
delay = d;
}
public void setFrame(int i){
currentFrame = i;
}
public void update(){
long elapsed = (System.nanoTime()-startTime)/1000000;
if(elapsed>delay){
currentFrame++;
startTime = System.nanoTime();
}
if(currentFrame == frames.length){
currentFrame = 0;
playedOnce = true;
}
}
public Bitmap getImage(){
return frames[currentFrame]; // line 36
}
public int getFrame(){
return currentFrame;
}
public boolean playedOnce(){
return playedOnce;
}
}
I understand the error, but how can I prevent it?
I appreciate every answer
The error is caused by the misconception that the length of an array is the same as the max index. Where length starts at 1 and index starts at 0, they always differ 1.
Change this:
if(currentFrame == frames.length ){
// your reset code
}
To this:
if(currentFrame == frames.length - 1){
// your reset code
}
Related
I have a javascript to calculate combination and works great. But when I tried to work on Android studio code it gets wrong result.
Js
var n=16;
var r=2;
function faktor(sayi) {
var sonuc=1;
for(var i=1;i<=sayi;i++)
{
sonuc*=i;
}
return sonuc;
}
function calculate (){
var sonuc2=faktor(n)/(faktor(r)*faktor(n-r));
console.log(sonuc2);
}
calculate();
Console log = 120
That's how I used in Android Studio
public static int tiklanan = 16;
public static int kombinasyon = 2;
int sonuc2;
private int faktor(int sayi){
int sonuc=1;
for(int i=1;i<=sayi;i++)
{
sonuc*=i;
}
return sonuc;
}
private void posibilityhesapla(){
sonuc2=faktor(tiklanan)/(faktor(kombinasyon)*faktor(tiklanan-kombinasyon));
Toast.makeText(this, "/"+sonuc2, Toast.LENGTH_SHORT).show();
}
sonuc2 always returns -1 but it must 120.
What I'm missing here ?
#Dorbagna your Integer variable has overflowed the value. Converting sonuc from int to long will fix your issue.
faktor(tiklanan) = faktor(16) = 20922789888000
While maximum value int can hold is 2147483647 (Integer.MAX_VALUE)
public static int tiklanan = 16;
public static int kombinasyon = 2;
// Change variable int type to long
long sonuc2;
//change return type to long
private long faktor(int sayi) {
// Change local variable int type to long
long sonuc = 1;
for (int i = 1; i <= sayi; i++) {
sonuc *= i;
}
return sonuc;
}
private void posibilityhesapla() {
sonuc2 = faktor(tiklanan) / (faktor(kombinasyon) * faktor(tiklanan - kombinasyon));
Toast.makeText(this, "/" + sonuc2, Toast.LENGTH_SHORT).show();
}
Im currently working on a little mobile game in which you throw spears at targets, and depending on how many spears you have left at the end of the level you get between one and three Runes (similar to the Star scoring sytem in Angry Birds). The plan is to save the runes and use them to unlock different spear variants in the Main Menu.
At the moment you can see how many runes you got after completeing the level, but the values are not yet being saved.
My question is, how would I go about saving the Runes after each Level? Any ideas? You can see the part of the script Im using for that below. Thanks in advance.
public int totalRunes = 0;
public GameObject rune1;
public GameObject rune2;
public GameObject rune3;
public void RuneCollection()
{
if (currentSpears >= 2)
{
Debug.Log("3 runes collected!");
rune3.SetActive(true);
totalRunes = 3;
}
else if (currentSpears >= 1)
{
Debug.Log("2 runes collected!");
rune2.SetActive(true);
totalRunes = 2;
}
else
{
Debug.Log("1 rune collected!");
rune1.SetActive(true);
totalRunes = 1;
}
}
I would use Unitys PlayerPrefs System.
Example:
Setting Score:
public Text score;
int currentscore = 0;
Increasing Score:
currentscore += 1;
score.text = currentscore.ToString();
Setting HighScore and Loading Menu:
GameManager.SetHighScore(currentscore);
SceneManager.LoadScene("Menu");
Functions to Load/Change PlayerPrefs:
void Start()
{
highscore.text = PlayerPrefs.GetInt("HighScore", 0).ToString();
}
public static void SetHighScore(int score)
{
if (score > PlayerPrefs.GetInt("HighScore", 0))
{
PlayerPrefs.SetInt("HighScore", score);
}
}
Your Code adapted to PlayerPrefs:
public int currentSpears = 0;
public GameObject rune1;
public GameObject rune2;
public GameObject rune3;
void Start()
{
currentSpears = PlayerPrefs.GetInt("CurrentSpears", 0).ToString();
}
public static void IncreaseSpears()
{
int spears = PlayerPrefs.GetInt("CurrentSpears", 0);
PlayerPrefs.SetInt("CurrentSpears", spears++);
}
public void RuneCollection()
{
if (currentSpears >= 2)
{
Debug.Log("3 runes collected!");
rune3.SetActive(true);
totalRunes = 3;
}
else if (currentSpears >= 1)
{
Debug.Log("2 runes collected!");
rune2.SetActive(true);
totalRunes = 2;
}
else
{
Debug.Log("1 rune collected!");
rune1.SetActive(true);
totalRunes = 1;
}
}
You can save your data with PlayerPrefs for some single values or PersistentDataPath for some multiple values such as a class. Find out more with this link, and you can use them as below.
PlayerPrefs:
private int score = 0;
//set value
PlayerPrefs.SetInt("Score", score);
//get value
private int savedScore = PlayerPrefs.GetInt("Score");
PersistentDataPath:
private string savedName;
private int savedHealth;
private string loadedName;
private int loadedHealth;
public void Save(){
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/FileName.dat",
FileMode.Create);
PlayerClass newData = new PlayerClass();
newData.health = savedHealth;
newData.name = savedName;
bf.Serialize(file, newData);
file.Close();
}
public void Load(){
if (File.Exists(Application.persistentDataPath + "/FileName.dat")){
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/FileName.dat", FileMode.Open);
ObjData newData = (ObjData)bf.Deserialize(file);
file.Close();
loadedHealth = newData.health;
loadedName = newData.name;
}
}
[Serializable]
class PlayerClass{
public string name;
public int health;
}
I'm trying to get the int value from a textView, the value inside will always be a number just in string, I set it like this
int randomNumber = rng.nextInt(6) + 1;
switch (randomNumber) {
case 1:
imageViewDice.setImageResource(R.drawable.dice1);
textView.setText("Tiraste un 1!");
if (player1.getText().equals("")) {
player1.setText("1");
} else {
player2.setText("1");
}
break;
//rest of cases 2, 3, 4, 5, & 6
I need the int value of this to be able to compare then to see which player (1 or 2) has the highest number, something like this
//this doesnt work
int value1 = Integer.parseInt(player1.getText().toString());
int value2 = Integer.parseInt(player2.getText().toString());
if (value1 > value2) {
result.setText("Jugador 1");
} else if (value1 < value2) {
result.setText("Jugador 2");
} else {
result.setText("Empate!");
}
What is the correct way to get the value so that the comparison can be made?
if you set static, you can very easily read in various classes:
player1.setText(Fraction.getP1()+"");
player2.setText(Fraction.getP2()+"");
if(Fraction.getP1()>Fraction.getP2())
result.setText("Jugador 1");
new class
public class Fraction {
private static int p1=0;
private static int p2=0;
public static int getP1(){
return p1;
}
public static int getP2(){
return p2;
}
public static void setP1(int p1fraction){
Fraction.p1 = p1fraction; //Fraction is your class name
}
public static void setP2(int p2fraction){
p2 = p2fraction;
}
}
I want to be able to play all audio from one app on only one channel on a device e.g only on left speakers.
I can do this with MediaPlayer playing an mp3 file like this to play only on left speaker
MediaPlayer mediaPlayer=MediaPlayer.create(this,R.raw.bitter_sweet);
mediaPlayer.setVolume(1.0f, 0f);
mediaPlayer.start();
But I want ALL audio from the app to play on left speaker, i.e if i add a webview and load a youtube url the audio should still play on one channel.
Using Exoplayer I can achieve this by creating a custom AudioProcessor adopted from the ChannelMappingAudioProcessor
public class StereoVolumeProcessor implements AudioProcessor {
private int channelCount;
private int sampleRateHz;
private int[] pendingOutputChannels;
private boolean active;
private int[] outputChannels;
private ByteBuffer buffer;
private ByteBuffer outputBuffer;
private boolean inputEnded;
private float[] volume;
private static final int LEFT_SPEAKER = 0;
private static final int RIGHT_SPEAKER = 1;
public StereoVolumeProcessor() {
buffer = EMPTY_BUFFER;
outputBuffer = EMPTY_BUFFER;
channelCount = Format.NO_VALUE;
sampleRateHz = Format.NO_VALUE;
}
public void setChannelMap(int[] outputChannels) {
pendingOutputChannels = outputChannels;
}
#Override
public boolean configure(int sampleRateHz, int channelCount, #C.Encoding int encoding)
throws UnhandledFormatException {
if(volume == null){
throw new IllegalStateException("volume has not been set! Call setVolume(float left,float right)");
}
boolean outputChannelsChanged = !Arrays.equals(pendingOutputChannels, outputChannels);
outputChannels = pendingOutputChannels;
if (outputChannels == null) {
active = false;
return outputChannelsChanged;
}
if (encoding != C.ENCODING_PCM_16BIT) {
throw new UnhandledFormatException(sampleRateHz, channelCount, encoding);
}
if (!outputChannelsChanged && this.sampleRateHz == sampleRateHz
&& this.channelCount == channelCount) {
return false;
}
this.sampleRateHz = sampleRateHz;
this.channelCount = channelCount;
active = true;
return true;
}
#Override
public boolean isActive() {
return active;
}
#Override
public int getOutputChannelCount() {
return outputChannels == null ? channelCount : outputChannels.length;
}
#Override
public int getOutputEncoding() {
return C.ENCODING_PCM_16BIT;
}
/**
* Returns the sample rate of audio output by the processor, in hertz. The value may change as a
* result of calling {#link #configure(int, int, int)} and is undefined if the instance is not
* active.
*/
#Override
public int getOutputSampleRateHz() {
return sampleRateHz;
}
#Override
public void queueInput(ByteBuffer inputBuffer) {
int position = inputBuffer.position();
int limit = inputBuffer.limit();
int size = limit - position;
if (buffer.capacity() < size) {
buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
} else {
buffer.clear();
}
if(isActive()){
int ch = 0;
for(int i = position;i<limit;i+=2){
short sample = (short) (inputBuffer.getShort(i)* volume[ch++]);
buffer.putShort(sample);
ch%=channelCount;
}
}else{
throw new IllegalStateException();
}
inputBuffer.position(limit);
buffer.flip();
outputBuffer = buffer;
}
#Override
public void queueEndOfStream() {
inputEnded = true;
}
/**
* Sets the volume of right and left channels/speakers
* The values are between 0.0 and 1.0
*
* #param left
* #param right
*/
public void setVolume(float left,float right){
volume = new float[]{left,right};
}
public float getLeftVolume(){
return volume[LEFT_SPEAKER];
}
public float getRightVolume(){
return volume[RIGHT_SPEAKER];
}
#Override
public ByteBuffer getOutput() {
ByteBuffer outputBuffer = this.outputBuffer;
this.outputBuffer = EMPTY_BUFFER;
return outputBuffer;
}
#SuppressWarnings("ReferenceEquality")
#Override
public boolean isEnded() {
return inputEnded && outputBuffer == EMPTY_BUFFER;
}
#Override
public void flush() {
outputBuffer = EMPTY_BUFFER;
inputEnded = false;
}
#Override
public void reset() {
flush();
buffer = EMPTY_BUFFER;
channelCount = Format.NO_VALUE;
sampleRateHz = Format.NO_VALUE;
outputChannels = null;
active = false;
}
}
Volume can be set with a value between 0.0 and 1.0 on either left, right or both speakers.
Use the processor as follows
stereoVolumeProcessor = new StereoVolumeProcessor();
stereoVolumeProcessor.setChannelMap(new int[]{0,1});
stereoVolumeProcessor.setVolume(1,0);
RenderersFactory factory = new DefaultRenderersFactory(this){
/**
* Builds an array of {#link AudioProcessor}s that will process PCM audio before output.
*/
#Override
protected AudioProcessor[] buildAudioProcessors() {
return new AudioProcessor[] {stereoVolumeProcessor};
}
};
I have ExoPlayer which plays HLS videos, the thing is i need to give user ability to change video quality(auto/1080/720/480).
I figured out that playing around with AdaptiveTrackSelection.Factory does set the quality, but it remains till the object is killed.
I have also tried using MappingTrackSelector, i know that my video has 4 tracks, but i did not get how to select any of it manually. Will this selection make it work?
Thanks for any ideas.
MappingTrackSelector.MappedTrackInfo trackInfo = mDefaultTrackSelector.getCurrentMappedTrackInfo();
mDefaultTrackSelector.selectTracks(
//what should go here?
, trackInfo.getTrackGroups(4));
Regarding this thread :https://github.com/google/ExoPlayer/issues/2250, I managed to change exo player video quality while playing previous one, so it does not getting in buffering instantly.
So I have next classes :
public enum HLSQuality {
Auto, Quality1080, Quality720, Quality480, NoValue
}
class HLSUtil {
private HLSUtil() {
}
#NonNull
static HLSQuality getQuality(#NonNull Format format) {
switch (format.height) {
case 1080: {
return HLSQuality.Quality1080;
}
case 720: {
return HLSQuality.Quality720;
}
case 480:
case 486: {
return HLSQuality.Quality480;
}
default: {
return HLSQuality.NoValue;
}
}
}
static boolean isQualityPlayable(#NonNull Format format) {
return format.height <= 1080;
}
}
public class ClassAdaptiveTrackSelection extends BaseTrackSelection {
public static final class Factory implements TrackSelection.Factory {
private final BandwidthMeter bandwidthMeter;
private final int maxInitialBitrate = 2000000;
private final int minDurationForQualityIncreaseMs = 10000;
private final int maxDurationForQualityDecreaseMs = 25000;
private final int minDurationToRetainAfterDiscardMs = 25000;
private final float bandwidthFraction = 0.75f;
private final float bufferedFractionToLiveEdgeForQualityIncrease = 0.75f;
public Factory(BandwidthMeter bandwidthMeter) {
this.bandwidthMeter = bandwidthMeter;
}
#Override
public ClassAdaptiveTrackSelection createTrackSelection(TrackGroup group, int... tracks) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality reset to Auto");
sHLSQuality = HLSQuality.Auto;
return new ClassAdaptiveTrackSelection(
group,
tracks,
bandwidthMeter,
maxInitialBitrate,
minDurationForQualityIncreaseMs,
maxDurationForQualityDecreaseMs,
minDurationToRetainAfterDiscardMs,
bandwidthFraction,
bufferedFractionToLiveEdgeForQualityIncrease
);
}
}
private static HLSQuality sHLSQuality = HLSQuality.Auto;
private final BandwidthMeter bandwidthMeter;
private final int maxInitialBitrate;
private final long minDurationForQualityIncreaseUs;
private final long maxDurationForQualityDecreaseUs;
private final long minDurationToRetainAfterDiscardUs;
private final float bandwidthFraction;
private final float bufferedFractionToLiveEdgeForQualityIncrease;
private int selectedIndex;
private int reason;
private ClassAdaptiveTrackSelection(TrackGroup group,
int[] tracks,
BandwidthMeter bandwidthMeter,
int maxInitialBitrate,
long minDurationForQualityIncreaseMs,
long maxDurationForQualityDecreaseMs,
long minDurationToRetainAfterDiscardMs,
float bandwidthFraction,
float bufferedFractionToLiveEdgeForQualityIncrease) {
super(group, tracks);
this.bandwidthMeter = bandwidthMeter;
this.maxInitialBitrate = maxInitialBitrate;
this.minDurationForQualityIncreaseUs = minDurationForQualityIncreaseMs * 1000L;
this.maxDurationForQualityDecreaseUs = maxDurationForQualityDecreaseMs * 1000L;
this.minDurationToRetainAfterDiscardUs = minDurationToRetainAfterDiscardMs * 1000L;
this.bandwidthFraction = bandwidthFraction;
this.bufferedFractionToLiveEdgeForQualityIncrease = bufferedFractionToLiveEdgeForQualityIncrease;
selectedIndex = determineIdealSelectedIndex(Long.MIN_VALUE);
reason = C.SELECTION_REASON_INITIAL;
}
#Override
public void updateSelectedTrack(long playbackPositionUs, long bufferedDurationUs, long availableDurationUs) {
long nowMs = SystemClock.elapsedRealtime();
// Stash the current selection, then make a new one.
int currentSelectedIndex = selectedIndex;
selectedIndex = determineIdealSelectedIndex(nowMs);
if (selectedIndex == currentSelectedIndex) {
return;
}
if (!isBlacklisted(currentSelectedIndex, nowMs)) {
// Revert back to the current selection if conditions are not suitable for switching.
Format currentFormat = getFormat(currentSelectedIndex);
Format selectedFormat = getFormat(selectedIndex);
if (selectedFormat.bitrate > currentFormat.bitrate
&& bufferedDurationUs < minDurationForQualityIncreaseUs(availableDurationUs)) {
// The selected track is a higher quality, but we have insufficient buffer to safely switch
// up. Defer switching up for now.
selectedIndex = currentSelectedIndex;
} else if (selectedFormat.bitrate < currentFormat.bitrate
&& bufferedDurationUs >= maxDurationForQualityDecreaseUs) {
// The selected track is a lower quality, but we have sufficient buffer to defer switching
// down for now.
selectedIndex = currentSelectedIndex;
}
}
// If we adapted, update the trigger.
if (selectedIndex != currentSelectedIndex) {
reason = C.SELECTION_REASON_ADAPTIVE;
}
}
#Override
public int getSelectedIndex() {
return selectedIndex;
}
#Override
public int getSelectionReason() {
return reason;
}
#Override
public Object getSelectionData() {
return null;
}
#Override
public int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) {
if (queue.isEmpty()) {
return 0;
}
int queueSize = queue.size();
long bufferedDurationUs = queue.get(queueSize - 1).endTimeUs - playbackPositionUs;
if (bufferedDurationUs < minDurationToRetainAfterDiscardUs) {
return queueSize;
}
int idealSelectedIndex = determineIdealSelectedIndex(SystemClock.elapsedRealtime());
Format idealFormat = getFormat(idealSelectedIndex);
// If the chunks contain video, discard from the first SD chunk beyond
// minDurationToRetainAfterDiscardUs whose resolution and bitrate are both lower than the ideal
// track.
for (int i = 0; i < queueSize; i++) {
MediaChunk chunk = queue.get(i);
Format format = chunk.trackFormat;
long durationBeforeThisChunkUs = chunk.startTimeUs - playbackPositionUs;
if (durationBeforeThisChunkUs >= minDurationToRetainAfterDiscardUs
&& format.bitrate < idealFormat.bitrate
&& format.height != Format.NO_VALUE && format.height < 720
&& format.width != Format.NO_VALUE && format.width < 1280
&& format.height < idealFormat.height) {
return i;
}
}
return queueSize;
}
private int determineIdealSelectedIndex(long nowMs) {
if (sHLSQuality != HLSQuality.Auto) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality seeking for " + String.valueOf(sHLSQuality));
for (int i = 0; i < length; i++) {
Format format = getFormat(i);
if (HLSUtil.getQuality(format) == sHLSQuality) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality set to " + String.valueOf(sHLSQuality));
return i;
}
}
}
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality seeking for auto quality " + String.valueOf(sHLSQuality));
long bitrateEstimate = bandwidthMeter.getBitrateEstimate();
long effectiveBitrate = bitrateEstimate == BandwidthMeter.NO_ESTIMATE
? maxInitialBitrate : (long) (bitrateEstimate * bandwidthFraction);
int lowestBitrateNonBlacklistedIndex = 0;
for (int i = 0; i < length; i++) {
if (nowMs == Long.MIN_VALUE || !isBlacklisted(i, nowMs)) {
Format format = getFormat(i);
if (format.bitrate <= effectiveBitrate && HLSUtil.isQualityPlayable(format)) {
Log.d(ClassAdaptiveTrackSelection.class.getSimpleName(), " Video player quality auto quality found " + String.valueOf(sHLSQuality));
return i;
} else {
lowestBitrateNonBlacklistedIndex = i;
}
}
}
return lowestBitrateNonBlacklistedIndex;
}
private long minDurationForQualityIncreaseUs(long availableDurationUs) {
boolean isAvailableDurationTooShort = availableDurationUs != C.TIME_UNSET
&& availableDurationUs <= minDurationForQualityIncreaseUs;
return isAvailableDurationTooShort
? (long) (availableDurationUs * bufferedFractionToLiveEdgeForQualityIncrease)
: minDurationForQualityIncreaseUs;
}
static void setHLSQuality(HLSQuality HLSQuality) {
sHLSQuality = HLSQuality;
}
}
Hope it helps someone.
You can check out ExoPlayer_TrackSelection from github for changing video quality manually.